From 8e839bb688190d92c98a1726a0dc9a2d25559ba8 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 7 Sep 2026 11:26:31 +0400 Subject: [PATCH 1/9] docs: propose typed core library and verified release pipeline --- .../2026-09-07-typescript-library-design.md | 248 ++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-07-typescript-library-design.md diff --git a/docs/superpowers/specs/2026-09-07-typescript-library-design.md b/docs/superpowers/specs/2026-09-07-typescript-library-design.md new file mode 100644 index 0000000..da1abb6 --- /dev/null +++ b/docs/superpowers/specs/2026-09-07-typescript-library-design.md @@ -0,0 +1,248 @@ +# TypeScript-библиотека Gothic Lock Solver + +Статус: **черновик для согласования, реализация не начата**. +Дата: 2026-09-07. Текущая задача ограничена нашим репозиторием. + +## Цель и границы + +Превратить проверенный решатель в читаемую, типизированную и воспроизводимо +публикуемую библиотеку. Потребитель подключает один готовый файл и получает +последовательность открытия замка по состоянию и связям. + +Референс сохраняется в `feat/matrix-astar-benchmarks`, commit +`3cbbeedbb409a2d8ffa5b00c778cfcb0b07c0ec6`. Код бенчмарка выполнен на доступном +commit `6b1cfbc13bcec68f609d45d5dc97504dd84f1760`; результаты находятся в +`docs/benchmarks/run-20260907T070258001Z-f78dff13`. + +Новая ветка `feat/typescript-core-library` начинается от референса. Её PR сначала +направляется в референсную ветку, чтобы показывать только преобразование библиотеки. +Слияние в `main`, активация публикации и сторонние PR в этот этап не входят. +Workflow публикации будет подготовлен и проверен; боевой выпуск запускается после +будущего одобренного слияния в `main`. Собственный сайт и UI не создаются. + +## Публичный контракт — предложение для утверждения + +```ts +type Command = Readonly<{ + plate: number; + direction: 'left' | 'right'; + steps: number; +}>; + +function solveLock( + state: readonly number[], + links: readonly (readonly number[])[], +): readonly Command[] | null; +``` + +Ровно два аргумента. N выводится из `state.length`; отдельные N, цель, алгоритм, +бюджеты, callbacks и настройки в публичный вызов не добавляются. Внешнее API +не возвращает телеметрию или оболочку с копиями входа. Типы диапазонов могут быть +уточнены при реализации без изменения формата данных JavaScript. + +**Предлагаемое представление состояния:** вектор позиций `[1,7,4,…]`, по одному +значению на пластину. Каждая строка визуального поля N×7 описывается одним числом. +Смысл этого выбора нужно подтвердить с пользователем перед реализацией: запрос +использует выражение «матрица состояния», а текущая модель хранит вектор. + +- Модель допускает N≥2; позиции — целые 1…7, цель — все 4. +- `links` имеет размер N×N, значения −1/0/1, диагональ 0. +- `links[source][target]` задаёт прямое направленное влияние без каскада. +- `plate` в командах начинается с 1; `steps` — целое 1…6. +- Сохраняется текущая координатная конвенция: `left` увеличивает позицию, + `right` уменьшает. Потребитель отвечает за соответствующее отображение в UI. +- Каждая команда и её промежуточные деления допустимы; входы не изменяются. +- `[]` означает уже открытый замок; `null` — доказанную недостижимость цели. +- Невалидный ввод и исчерпание ресурсов дают различимые документированные ошибки. + Ресурсный предел никогда не преобразуется в `null`. +- Функция синхронна, детерминирована и безопасна для повторных независимых вызовов. + Для тяжёлых входов потребитель может вызвать её в Worker. + +Пределы конкретного поиска отделены от механики. Текущая безопасная числовая +кодировка допускает N≤18; расширение кодировки сверх этого не является задачей +миграции. Нет специального ограничения N≤8 в модели библиотеки. + +## Алгоритм и структура + +Сохраняем точную рациональную матричную подготовку, достаточный сертификат пути, +A* с согласованной нижней оценкой и точный BFS для вырожденной матрицы. +Основная гарантия — минимум количества действий A. Разные выбранные пластины U +и единичные сдвиги C измеряются отдельно; общий минимум U/C среди равных по A +путей не обещается. + +Применяем SOLID, KISS, YAGNI и DRY к текущей задаче: + +- один публичный facade и одна реализация механики; +- объект модели замка отвечает за проверенный неизменяемый ввод и эффекты; +- стратегии A* и BFS имеют небольшой внутренний общий контракт; +- приоритетная очередь и бюджет поиска инкапсулируют своё состояние; +- точная арифметика и преобразования остаются чистыми функциями там, где класс + не даёт дополнительной ответственности; +- композиция без иерархий ради ООП, DI-контейнеров, реестров и плагинной системы; +- CLI остаётся отдельным адаптером и не входит в распространяемое ядро; +- диагностические инструменты бенчмарка не расширяют публичные два аргумента. + +Исходники — TypeScript в strict mode, без `any` и необоснованных подавлений +проверок. Runtime-зависимостей нет; инструменты разработки являются devDependencies. + +## Инструменты и политика версий + +Используются **последние стабильные релизы** на момент начала реализации. +Проверка npm `latest` от 2026-09-07 дала: + +| Инструмент | Версия | Назначение | +|---|---:|---| +| TypeScript | 7.0.2 | Типизация, проверка и декларации | +| Vite | 8.2.2 | Library build | +| Vitest | 5.0.0 | Unit/integration/browser tests и benchmark runner | +| @vitest/browser-playwright | 5.0.0 | Настоящие браузеры | +| @vitest/coverage-v8 | 5.0.0 | Отчёт покрытия | +| Playwright | 1.63.0 | Browser provider и E2E подключения | +| Oxlint | 1.81.0 | Линтинг JavaScript/TypeScript | + +Перед установкой версии перепроверяются, затем прямые зависимости фиксируются +точно и сохраняется `pnpm-lock.yaml`. CI использует frozen lockfile. Обновления +зависимостей проходят тот же набор проверок, без автоматического обновления +baseline для сокрытия регрессии. Никаких обходов peerDependencies через `--force`. + +Причина выбора Oxlint: текущий typescript-eslint 8.69.0 объявляет поддержку +TypeScript `>=4.8.4 <6.1.0`; это несовместимо с требованием TypeScript 7 latest. +Сборка Vite не заменяет проверку типов: отдельный typecheck обязателен. +Среда разработки — актуальный Node.js LTS, поддерживаемый всем инструментарием; +начальная CI-матрица runtime — Node.js 24 и 26 с закреплением версий. + +## Четыре распространяемых файла + +| Файл | Подключение | +|---|---| +| `gothic-lock-solver.mjs` | Читаемый ESM: браузер или Node.js import | +| `gothic-lock-solver.min.mjs` | Минифицированный ESM | +| `gothic-lock-solver.js` | Читаемый classic script, глобальный `GothicLockSolver.solveLock` | +| `gothic-lock-solver.min.js` | Минифицированный classic script | + +Каждый файл самодостаточен: без внешних imports, дополнительных chunks, обращения +к сети, Node API и UI. Сборка использует один TypeScript entry point, форматы +ESM и IIFE. Минификация не меняет контракт, ошибки и найденный результат. +Текущий ручной ESM-assembler заменяется стандартной сборкой Vite. + +Проверяется настоящая минификация обоих `.min` файлов: стандартная настройка +Vite library mode сама по себе может сохранять пробелы в ESM. Если нужен отдельный +финальный проход минификатора, он входит в нашу сборку и проверяется на выходе. + +Декларации типов для ESM-путей, описание глобального API, лицензия и manifest +с версией, commit SHA и хэшами сопровождают выпуск. Это вспомогательные файлы; +для исполнения потребитель выбирает ровно один из четырёх runtime-файлов. + +## Проверки реального использования + +Vitest projects разделяют Node и настоящий браузер через Playwright. `jsdom` +или изолированный Node VM не считаются подтверждением браузерной совместимости. +Начальная браузерная матрица: Chromium, Firefox, WebKit. + +1. Общий каталог всех 45 входов проверяется в Node и Browser Mode. Каждый путь + независимо воспроизводится по одному делению и сверяется с точным минимумом A. +2. Малые модели и контрпримеры защищают границы, матричные ложноположительные + выводы, вырожденность, недостижимость и повторный выбор пластины. +3. Проверяются именно собранные файлы: оба ESM в Node, все четыре варианта + в браузерах. Для каждого проверяются 45 входов и одинаковый публичный контракт. +4. E2E загружает реальные неизменённые байты dist через HTTP, classic script + и `script type=module`, а не повторно преобразованный dev-server исходник. + Проверяются отсутствие дополнительных runtime-загрузок и работа Worker. +5. Отдельный небольшой сценарий проверяет classic script через `file://`; + локальный ESM через `file://` не обещается. +6. Неподдерживаемая среда или невозможность запустить браузер явно отражаются + в результате. Пропущенная обязательная среда не даёт статуса полной готовности. + +Покрытие публикуется как диагностический отчёт. Число тестов или процент покрытия +не подменяют независимую проверку механики и производственного импорта. + +## Бенчмарк и snapshot + +Сохраняем текущие 45 fixtures, происхождение данных, точный baseline и сторонний +решатель на закреплённом commit. Его код читается из отдельного checkout и +проверяется хэшами; AGPL-исходник не включается в наш Apache-репозиторий. + +На каждом PR сравниваются candidate и base в одной среде, с прогревом, +чередованием порядка и повторными измерениями. Исторический snapshot остаётся +доказательством прошлых измерений, но не заменяет свежее парное сравнение. +Статистики разных браузеров, Node и разных машин не смешиваются. + +Для каждого замка и реализации отчёт содержит A/U/C, переключения, отдельные +замеры времени, median/p95, параметры выборки, версию среды и хэши артефактов. +Также сохраняются размер bundle и измерения памяти с точным названием метрики. + +Память Node измеряется отдельным запуском в изолированном процессе: peak RSS +включает runtime и не выдаётся за точное число байтов, выделенных алгоритмом. +Для браузеров портативного точного peak-heap измерения не обещаем: значение +отмечается как недоступное либо приводится отдельно с указанием API и ограничений. +Замер памяти отделён от замера скорости, чтобы инструментация не искажала таймер. + +**Правила сравнения:** + +- Неверный путь, потеря решаемости, рост A относительно установленного минимума, + несоответствие контракту или артефакту блокируют CI. +- A/U/C сверяются по каждому замку. При равном A ухудшение U/C требует явного + обоснования и отдельного принятого изменения snapshot. Миграция в TypeScript + сама по себе не является таким обоснованием. +- Одиночное отклонение времени/RSS на общей CI-машине не считается доказанной + регрессией. Отчёт помечает сигнал, проверяет шум и выполняет ограниченный повтор. +- Допуски времени и памяти калибруются на сравнении неизменённой версии с собой + до включения performance verdict. Они не выбираются ради зелёного результата. +- Детерминированные ресурсные лимиты — строгая проверка. Измеряемые время/RSS — + отдельный performance verdict с оценкой достоверности, видимый в PR. +- Snapshot не перезаписывается автоматически тестами. Его обновление — отдельная + команда и проверяемый diff с источником, причиной и результатами сравнения. +- Для сравнения используется baseline целевой ветки PR, чтобы candidate не мог + скрыть ухудшение простым изменением собственного ожидаемого файла. + +## CI и публикация + +PR запускает lint, typecheck, unit/integration, сборку, тесты dist/E2E и сравнение +бенчмарка. JSON/Markdown, покрытие и четыре файла доступны как CI artifacts. +Документируется одна команда локальной полной проверки и отдельные команды этапов. +PR не публикует боевой `latest` и не получает права деплоя. + +После будущего слияния в `main` workflow повторно подтверждает проверки точного +merge commit и публикует проверенные байты на GitHub Pages. Публикация не требует +npm и не добавляет сборку потребителю. Предлагаемый адрес имеет вид +`https://marcus-rise.github.io/gothic-lock-solver/releases//`. +Это планируемый URL, сейчас он не является опубликованным продуктом. + +Релизы по commit SHA сохраняются при следующих deployments. Отдельная ветка +публикации может хранить накопленный статический дистрибутив; source-ветка хранит +TypeScript, конфигурацию и принятые benchmark snapshots. `latest` допустим для +демонстраций; интеграции закрепляют commit или неизменяемую версию. + +После публикации проверяются реальные URL: MIME JavaScript для `.mjs`, CORS +для импорта с другого origin, SHA256 и поведение одного известного замка. +Если GitHub Pages в этом окружении не удовлетворяет условиям, это фиксируется +до объявления CDN-контракта готовым. Обычная GitHub blob-ссылка не является +ссылкой на исполняемый файл. GitHub Release с файлами может служить архивом выпуска. + +## Документация и приёмка + +README содержит назначение, алгоритмические гарантии, оба входа и все исходы, +координатные конвенции, четыре способа подключения, Worker, поддержанные среды, +команды проверки, чтение performance verdict, изменение snapshot и выпуск. +AGENTS.md описывает реальные модули, инварианты, команды и границы изменений. +Для работы с публичным проектом доступ к частной wiki не требуется. + +Готовность подтверждается проходящим CI нового PR, проверкой всех 45 входов, +принятыми performance-результатами, тестами всех публикуемых форматов и понятным +планом первого выпуска. До согласованного merge публичная CDN-публикация не +объявляется выполненной. + +## Основания + +- Прочитаны инженерные принципы владельца: SOLID/KISS/YAGNI/DRY, реальные пути + импорта и проверка распространяемого артефакта; разделение deterministic и + environmental verdict. Использованы общие правила, помеченные public_safe. +- [Vite library build options](https://vite.dev/config/build-options) +- [Vite и проверка TypeScript](https://vite.dev/guide/features#typescript) +- [Vitest Browser Mode](https://vitest.dev/guide/browser/) +- [Vitest benchmarking](https://vitest.dev/guide/benchmarking) +- [typescript-eslint: поддержанные версии](https://typescript-eslint.io/users/dependency-versions/) +- [Oxlint](https://oxc.rs/docs/guide/usage/linter.html) +- [GitHub Pages workflows](https://docs.github.com/en/pages/getting-started-with-github-pages/using-custom-workflows-with-github-pages) +- Версии выше получены из npm registry `//latest` 2026-09-07; + peerDependencies и engines прочитаны до предложения стека. From 6b7e486087581d4292ddc30255cb43e2759765e6 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 7 Sep 2026 11:48:02 +0400 Subject: [PATCH 2/9] docs: clarify mathematical tuple contract and release artifacts --- .../2026-09-07-typescript-library-design.md | 193 ++++++++++++++---- 1 file changed, 157 insertions(+), 36 deletions(-) diff --git a/docs/superpowers/specs/2026-09-07-typescript-library-design.md b/docs/superpowers/specs/2026-09-07-typescript-library-design.md index da1abb6..081541b 100644 --- a/docs/superpowers/specs/2026-09-07-typescript-library-design.md +++ b/docs/superpowers/specs/2026-09-07-typescript-library-design.md @@ -20,37 +20,43 @@ commit `6b1cfbc13bcec68f609d45d5dc97504dd84f1760`; результаты нахо Workflow публикации будет подготовлен и проверен; боевой выпуск запускается после будущего одобренного слияния в `main`. Собственный сайт и UI не создаются. -## Публичный контракт — предложение для утверждения +## Публичный контракт — уточнённое предложение ```ts -type Command = Readonly<{ - plate: number; - direction: 'left' | 'right'; - steps: number; -}>; +type Position = 1 | 2 | 3 | 4 | 5 | 6 | 7; +type Link = -1 | 0 | 1; +type Delta = -6 | -5 | -4 | -3 | -2 | -1 | 1 | 2 | 3 | 4 | 5 | 6; +type State = readonly [Position, Position, ...Position[]]; +type Command = readonly [index: number, delta: Delta]; function solveLock( - state: readonly number[], - links: readonly (readonly number[])[], + state: State, + links: readonly (readonly Link[])[], ): readonly Command[] | null; ``` Ровно два аргумента. N выводится из `state.length`; отдельные N, цель, алгоритм, бюджеты, callbacks и настройки в публичный вызов не добавляются. Внешнее API -не возвращает телеметрию или оболочку с копиями входа. Типы диапазонов могут быть -уточнены при реализации без изменения формата данных JavaScript. +не возвращает телеметрию или оболочку с копиями входа. Для литерального tuple N +типизация дополнительно проверяет размер links, выводя его только из state. +Для данных динамической длины обязательна runtime-проверка квадратности. -**Предлагаемое представление состояния:** вектор позиций `[1,7,4,…]`, по одному +**Подтверждённое представление состояния:** tuple позиций `[1,7,4,…]`, по одному значению на пластину. Каждая строка визуального поля N×7 описывается одним числом. -Смысл этого выбора нужно подтвердить с пользователем перед реализацией: запрос -использует выражение «матрица состояния», а текущая модель хранит вектор. +Пользователь подтвердил этот формат и предложил компактную команду из двух чисел. - Модель допускает N≥2; позиции — целые 1…7, цель — все 4. - `links` имеет размер N×N, значения −1/0/1, диагональ 0. - `links[source][target]` задаёт прямое направленное влияние без каскада. -- `plate` в командах начинается с 1; `steps` — целое 1…6. -- Сохраняется текущая координатная конвенция: `left` увеличивает позицию, - `right` уменьшает. Потребитель отвечает за соответствующее отображение в UI. +- `index` в командах — целый индекс 0…N−1, совпадающий с индексом управляемой + позиции state и строкой links. В модели замка это выбранная пластина; + для подписи «пластина №1» UI прибавляет единицу. +- Значения Position сохраняют игровую нумерацию 1…7, цель 4. Нумерация + элементов массива и нумерация отверстий — разные части контракта. +- `delta > 0` увеличивает выбранную числовую позицию; `delta < 0` уменьшает. + В публичном математическом контракте нет физических направлений left/right. + `abs(delta)` — число делений, максимум 6. Нулевая delta не возвращается: + отсутствие действий уже выражается пустым массивом. - Каждая команда и её промежуточные деления допустимы; входы не изменяются. - `[]` означает уже открытый замок; `null` — доказанную недостижимость цели. - Невалидный ввод и исчерпание ресурсов дают различимые документированные ошибки. @@ -58,6 +64,49 @@ function solveLock( - Функция синхронна, детерминирована и безопасна для повторных независимых вызовов. Для тяжёлых входов потребитель может вызвать её в Worker. +### Математические координаты — обязательный инвариант + +По последнему уточнению пользователя ядро работает с изменением числовой позиции +штифта. Перевод в физическое движение пластины принадлежит интегратору. +Команда `[i, delta]` задаёт: + +```text +x'_j = x_j + delta × (j === i ? 1 : links[i][j]) +x' = x + delta × (I + linksᵀ) × e_i +``` + +Границы проверяются для всех затронутых пластин; при нарушении движение целиком +недопустимо. Связи не замыкаются транзитивно. Разворот строк в UI не изменяет +знак команды или индексы в данных. + +Контрольный асимметричный пример: + +```ts +solveLock([6, 2], [[0, -1], [0, 0]]) +// [[0, -2]]: первая позиция 6→4; реверсивная связь меняет вторую 2→4. +``` + +Точное преобразование старого результата для сравнения: +`index = old.plate - 1`, +`delta = old.direction === 'left' ? old.steps : -old.steps`. +Для исходного единичного хода UnlockMyLoot: `delta = d`. +Его UI при показе физического движения пластины использует обратный знак. +Этот обратный знак применяется только в клиентском адаптере, не к state, +links или командам ядра. Названия left/right старой реализации не переносятся +в новый публичный контракт. + +Выбор координат не меняет критерий оптимизации. Одно `[index, delta]` стоит +одно действие A независимо от `abs(delta)`; C равно сумме `abs(delta)`, +U — количеству разных index. Интегратор может менять подписи и направление +отображения, но не переставлять команды произвольно: порядок защищает границы. +Минимум действий находит ядро. Выбор другой целевой функции был бы отдельным +изменением алгоритмического контракта, а не настройкой отображения. + +Проверки закрепляют пример выше, отсутствие обратной связи, атомарное отклонение +выхода любой связанной пластины за 1…7, отсутствие каскада, пустой результат, +доказанную недостижимость и отсутствие нулевых команд. Это намеренное изменение +публичного контракта; README и release notes должны объяснять миграцию. + Пределы конкретного поиска отделены от механики. Текущая безопасная числовая кодировка допускает N≤18; расширение кодировки сверх этого не является задачей миграции. Нет специального ограничения N≤8 в модели библиотеки. @@ -82,8 +131,27 @@ A* с согласованной нижней оценкой и точный BFS - CLI остаётся отдельным адаптером и не входит в распространяемое ядро; - диагностические инструменты бенчмарка не расширяют публичные два аргумента. -Исходники — TypeScript в strict mode, без `any` и необоснованных подавлений -проверок. Runtime-зависимостей нет; инструменты разработки являются devDependencies. +### Строгость, которая проверяется инструментами + +Исходники — TypeScript с `strict: true`, `noUncheckedIndexedAccess`, +`exactOptionalPropertyTypes`, `noImplicitOverride`, `noImplicitReturns`, +`noFallthroughCasesInSwitch`, `noPropertyAccessFromIndexSignature`, +`noUnusedLocals`, `noUnusedParameters`, `noUncheckedSideEffectImports`, +`verbatimModuleSyntax` и `isolatedModules`. Unreachable code и unused labels +запрещены; `skipLibCheck: false`. Применимость флагов проверяется выбранным +актуальным компилятором, без отключения диагностики ради сборки. + +Нет `any`, `@ts-ignore`, необоснованных type assertions и non-null assertions. +`@ts-expect-error` допустим только в отрицательных тестах типового контракта: +тест должен доказывать, что недопустимый вызов отвергается компилятором. +На runtime-границе проверяются целочисленность, диапазоны, длины и отсутствие +дыр в массивах. `readonly` в типах не подменяет эти проверки или проверку +отсутствия мутации входов. Ошибки линтера и обязательных проверок блокируют CI. + +Матричная алгебра точная, без epsilon и приблизительных сравнений ранга. +Поиск не подменяется эвристическим приблизительным ответом при исчерпании +ресурсов. Математические гарантии обосновываются отдельно от тестовых примеров. +Runtime-зависимостей нет; инструменты разработки являются devDependencies. ## Инструменты и политика версий @@ -139,6 +207,12 @@ Vitest projects разделяют Node и настоящий браузер ч или изолированный Node VM не считаются подтверждением браузерной совместимости. Начальная браузерная матрица: Chromium, Firefox, WebKit. +Agent Browser рассмотрен отдельно: это CLI управления настоящим браузером для +агента, а не математический тестовый runner и не симулятор DOM. Для обязательной +матрицы CI остаётся документированный провайдер Vitest + Playwright. Agent Browser +может использоваться при дополнительной диагностике реального подключения; +вторая обязательная параллельная система тестов не создаётся. + 1. Общий каталог всех 45 входов проверяется в Node и Browser Mode. Каждый путь независимо воспроизводится по одному делению и сверяется с точным минимумом A. 2. Малые модели и контрпримеры защищают границы, матричные ложноположительные @@ -190,34 +264,75 @@ Vitest projects разделяют Node и настоящий браузер ч до включения performance verdict. Они не выбираются ради зелёного результата. - Детерминированные ресурсные лимиты — строгая проверка. Измеряемые время/RSS — отдельный performance verdict с оценкой достоверности, видимый в PR. +- Performance verdict различает `passed`, `regression` и `inconclusive`. + Недостаточная достоверность не превращается в заявление «регрессии нет»; + результаты и причина неопределённости сохраняются. Готовность выпуска требует + принятого результата сравнения, а не только зелёных unit tests. - Snapshot не перезаписывается автоматически тестами. Его обновление — отдельная команда и проверяемый diff с источником, причиной и результатами сравнения. - Для сравнения используется baseline целевой ветки PR, чтобы candidate не мог скрыть ухудшение простым изменением собственного ожидаемого файла. -## CI и публикация +## CI, GitHub Releases и доставка в браузер PR запускает lint, typecheck, unit/integration, сборку, тесты dist/E2E и сравнение бенчмарка. JSON/Markdown, покрытие и четыре файла доступны как CI artifacts. Документируется одна команда локальной полной проверки и отдельные команды этапов. PR не публикует боевой `latest` и не получает права деплоя. -После будущего слияния в `main` workflow повторно подтверждает проверки точного -merge commit и публикует проверенные байты на GitHub Pages. Публикация не требует -npm и не добавляет сборку потребителю. Предлагаемый адрес имеет вид -`https://marcus-rise.github.io/gothic-lock-solver/releases//`. -Это планируемый URL, сейчас он не является опубликованным продуктом. - -Релизы по commit SHA сохраняются при следующих deployments. Отдельная ветка -публикации может хранить накопленный статический дистрибутив; source-ветка хранит -TypeScript, конфигурацию и принятые benchmark snapshots. `latest` допустим для -демонстраций; интеграции закрепляют commit или неизменяемую версию. - -После публикации проверяются реальные URL: MIME JavaScript для `.mjs`, CORS -для импорта с другого origin, SHA256 и поведение одного известного замка. -Если GitHub Pages в этом окружении не удовлетворяет условиям, это фиксируется -до объявления CDN-контракта готовым. Обычная GitHub blob-ссылка не является -ссылкой на исполняемый файл. GitHub Release с файлами может служить архивом выпуска. +**Канонический выпуск — GitHub Release.** Pages из предлагаемой схемы убран. +После будущего слияния в `main` workflow проверяет точный merge commit, создаёт +draft release, прикладывает четыре runtime-файла, декларации, контрольные суммы +и `benchmark-evidence.json`. Краткая таблица изменений из evidence попадает +в release notes. Затем после обязательных проверок draft публикуется. + +Пятый evidence-файл не является зависимостью ядра и не загружается потребителем +при solveLock. У него версионированная JSON-схема: версия выпуска/source SHA, +SHA предыдущего сравниваемого выпуска, fixture/source hashes, версии сред, +параметры измерения, итоги A/U/C и времени/памяти, дельты, quality/performance +verdict и ссылки/хэши подробного отчёта. Для первого выпуска сравнение с нашим +закреплённым референсом явно называется reference baseline, а не предыдущим релизом. +Сторонний pinned baseline в evidence обозначается отдельно. + +Файл evidence нужен даже при наличии сводки в release notes: JSON можно проверять +автоматически, а текст описания релиза может редактироваться. Если в репозитории +включены immutable releases, весь набор assets прикладывается до публикации. +Неизменяемость не объявляется настроенной без проверки фактической настройки. + +**GitHub Packages рассмотрен и не выбран:** его npm registry требует +аутентификацию даже для установки public packages. Это не анонимный endpoint +для браузерного ` + +``` + +Второй вариант использует локальный `.mjs` в `script type="module"` через import. +Все четыре формата проверяются на статическом HTTP-сервере в реальных браузерах; +это проверка файлов потребителя, а не обещание удалённого CDN. Обновление интеграции +означает явную замену версии/файла, откат — возврат к прежней проверенной сборке. + +Файл evidence остаётся отдельным Release asset и не входит в runtime-загрузку. +Манифест контрольных сумм связывает npm-версию и source SHA с байтами runtime-файлов. +Release notes содержат ссылку на npm-версию, команду установки, ссылки скачивания +и примеры подключения локального файла. ## Документация и приёмка @@ -344,8 +432,8 @@ AGENTS.md описывает реальные модули, инварианты Готовность подтверждается проходящим CI нового PR, проверкой всех 45 входов, принятыми performance-результатами, тестами всех публикуемых форматов и понятным -планом первого выпуска. До согласованного merge публичная CDN-публикация не -объявляется выполненной. +планом первого выпуска. До согласованного merge стабильная публикация npm/Release +не выполняется; настройка канареек также не считается уже выполненной публикацией. ## Основания @@ -361,9 +449,14 @@ AGENTS.md описывает реальные модули, инварианты - [Agent Browser](https://github.com/vercel-labs/agent-browser) - [Vitest Playwright provider](https://vitest.dev/config/browser/playwright) - [TypeScript strict](https://www.typescriptlang.org/tsconfig/strict.html) -- [GitHub Packages permissions](https://docs.github.com/en/packages/learn-github-packages/about-permissions-for-github-packages) +- [Публичные npm-пакеты](https://docs.npmjs.com/about-public-packages/) +- [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) +- [npm publish: tarball и неизменяемость версии](https://docs.npmjs.com/cli/v11/commands/npm-publish/) +- [GitHub Actions: закрепление SHA и обновления](https://docs.github.com/en/actions/reference/security/secure-use) +- [Официальный GitHub CLI: создание Release](https://cli.github.com/manual/gh_release_create) - [GitHub Release assets](https://docs.github.com/en/rest/releases/assets) - [GitHub immutable releases](https://docs.github.com/en/code-security/concepts/supply-chain-security/immutable-releases) -- [jsDelivr GitHub delivery](https://github.com/jsdelivr/jsdelivr#github) +- [Официальный npm registry: metadata и tarball](https://github.com/npm/registry/blob/main/docs/responses/package-metadata.md) +- [npm dist-tags для stable/canary](https://docs.npmjs.com/adding-dist-tags-to-packages/) - Версии выше получены из npm registry `//latest` 2026-09-07; peerDependencies и engines прочитаны до предложения стека. From f6f0e54e0887c3801c50b16e4a15b88b166a0804 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 7 Sep 2026 12:05:30 +0400 Subject: [PATCH 4/9] docs: require free browser CDN and document npm mirroring --- .../2026-09-07-typescript-library-design.md | 98 ++++++++++++++----- 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/docs/superpowers/specs/2026-09-07-typescript-library-design.md b/docs/superpowers/specs/2026-09-07-typescript-library-design.md index 1e2dcfc..042f558 100644 --- a/docs/superpowers/specs/2026-09-07-typescript-library-design.md +++ b/docs/superpowers/specs/2026-09-07-typescript-library-design.md @@ -321,15 +321,41 @@ PR запускает lint, typecheck, unit/integration, сборку, тест Документируется одна команда локальной полной проверки и отдельные команды этапов. PR не публикует боевой `latest` и не получает права деплоя. -**Последнее требование пользователя: публичный npmjs.org, затем GitHub Release.** +**Последнее требование: удалённый CDN URL обязателен, вся схема бесплатна.** +Интегратор вставляет script tag в свой HTML без скачивания/копирования bundle, +менеджера пакетов, сборки, аккаунта или токена. Локальный vendor-файл не заменяет +этот критерий приёмки. npmjs.org остаётся каналом публикации пакета; после него +создаётся GitHub Release с теми же файлами и benchmark evidence. + Публичный пакет в npmjs.org предназначен для установки и скачивания tarball. GitHub хранит исходники, CI и релизы с готовыми файлами и доказательным отчётом. -Сторонний CDN, GitHub Packages, Pages и отдельная ветка `distribution` исключены. -Исходная идея прямого ` + ``` -Второй вариант использует локальный `.mjs` в `script type="module"` через import. -Все четыре формата проверяются на статическом HTTP-сервере в реальных браузерах; -это проверка файлов потребителя, а не обещание удалённого CDN. Обновление интеграции -означает явную замену версии/файла, откат — возврат к прежней проверенной сборке. +Модульный вариант использует CDN URL `.mjs` в `script type="module"` через import. +Node.js импортирует установленный пакет или локальный `.mjs`; прямой HTTPS-import +стандартным Node loader не входит в контракт. Все четыре формата до публикации +проверяются на статическом HTTP-сервере, после публикации — по реальным CDN URL. +Ссылки закрепляют точную версию; обновление и откат требуют явной замены версии. +Четыре файла существуют в npm tarball заранее. CDN не выполняет нашу минификацию +или сборку: не используем +esm, combine, сокращённый путь и автогенерацию .min. Файл evidence остаётся отдельным Release asset и не входит в runtime-загрузку. Манифест контрольных сумм связывает npm-версию и source SHA с байтами runtime-файлов. Release notes содержат ссылку на npm-версию, команду установки, ссылки скачивания -и примеры подключения локального файла. +и готовые CDN script/import snippets. + +Дополнительная read-only проверка 2026-09-07: у dayjs@1.11.13 файл dayjs.min.js +из npm tarball и `https://cdn.jsdelivr.net/npm/dayjs@1.11.13/dayjs.min.js` совпал +побайтно (7160 bytes, SHA256 +`9cfdb93f38afcf2d076abecd66d32bfd3383cdf1967654ebc26a26605daf4173`). +CDN ответил 200, `Content-Type: application/javascript; charset=utf-8`, +`Access-Control-Allow-Origin: *` на запрос с Origin: https://example.com. +Эта проверка иллюстрирует механизм сервиса и не заменяет проверки нашей сборки. ## Документация и приёмка @@ -458,5 +505,10 @@ AGENTS.md описывает реальные модули, инварианты - [GitHub immutable releases](https://docs.github.com/en/code-security/concepts/supply-chain-security/immutable-releases) - [Официальный npm registry: metadata и tarball](https://github.com/npm/registry/blob/main/docs/responses/package-metadata.md) - [npm dist-tags для stable/canary](https://docs.npmjs.com/adding-dist-tags-to-packages/) +- [GitHub Packages npm: обязательная авторизация](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#authenticating-to-github-packages) +- [jsDelivr: автоматическая раздача npm](https://github.com/jsdelivr/jsdelivr#npm) +- [UNPKG: документированный URL отдельного файла](https://unpkg.com/) +- [Бесплатность публичных npm-пакетов](https://docs.npmjs.com/about-npm/#sharing-packages-and-collaborating-with-others) +- [GitHub Actions: бесплатные standard runners и квоты](https://docs.github.com/en/billing/concepts/product-billing/github-actions) - Версии выше получены из npm registry `//latest` 2026-09-07; peerDependencies и engines прочитаны до предложения стека. From 91c5a12ab00d183911c2432a7e05fda80684068b Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 7 Sep 2026 12:09:54 +0400 Subject: [PATCH 5/9] docs: name the public package gothic-lock-solver --- .../2026-09-07-typescript-library-design.md | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/superpowers/specs/2026-09-07-typescript-library-design.md b/docs/superpowers/specs/2026-09-07-typescript-library-design.md index 042f558..e3ee9fa 100644 --- a/docs/superpowers/specs/2026-09-07-typescript-library-design.md +++ b/docs/superpowers/specs/2026-09-07-typescript-library-design.md @@ -3,6 +3,13 @@ Статус: **черновик для согласования, реализация не начата**. Дата: 2026-09-07. Текущая задача ограничена нашим репозиторием. +Название, выбранное пользователем: **Gothic Lock Solver**. +Публичное npm-имя: **`gothic-lock-solver`** (без scope). +Имена новых npm-пакетов записываются строчными URL-safe символами, без пробелов. +Проверка `https://registry.npmjs.org/gothic-lock-solver` от 2026-09-07 вернула +404 `Not found`: опубликованный пакет под этим именем не найден. Имя пока +не зарегистрировано нами; возможность первой публикации перепроверяется перед выпуском. + ## Цель и границы Превратить проверенный решатель в читаемую, типизированную и воспроизводимо @@ -359,8 +366,8 @@ node_modules и браузерные бинарники в отчётные arti После будущего слияния в `main` workflow проверяет точный merge commit. Для выпуска используется одна согласованная SemVer-версия в package.json, npm и GitHub tag. -Имя npm-пакета и права на его scope проверяются до первой публикации; -совпадение GitHub username с npm username не предполагается. +До первой публикации проверяются доступность имени `gothic-lock-solver` и +npm-аккаунт владельца. Совпадение GitHub username с npm username не предполагается. Последовательность выпуска: @@ -427,7 +434,7 @@ verdict и ссылки/хэши подробного отчёта. Для пе декларации; пакет имеет явный ESM export. Classic IIFE не выдаётся за CommonJS. 2. Вставить в HTML script/import с прямым URL файла на CDN. Для предлагаемого jsDelivr это URL точной версии и полного пути к готовому файлу: - `https://cdn.jsdelivr.net/npm/@/dist/gothic-lock-solver.min.js`. + `https://cdn.jsdelivr.net/npm/gothic-lock-solver@/dist/gothic-lock-solver.min.js`. Аналогично подключаются читаемый .js и оба .mjs. UNPKG также поддерживает эту архитектуру: `https://unpkg.com/@/`. @@ -438,11 +445,11 @@ host `cdn.jsdelivr.net` и префикс `/npm/`. Утверждения «лу предложении один основной CDN — jsDelivr; двойной загрузчик и автоматическое переключение между CDN не добавляются. -Шаблон обязательного classic-подключения (PACKAGE/VERSION будут заменены -подтверждёнными именем и версией после первого выпуска): +Шаблон обязательного classic-подключения (VERSION будет заменена точной версией +после первого проверенного выпуска): ```html - + @@ -497,6 +504,7 @@ AGENTS.md описывает реальные модули, инварианты - [Vitest Playwright provider](https://vitest.dev/config/browser/playwright) - [TypeScript strict](https://www.typescriptlang.org/tsconfig/strict.html) - [Публичные npm-пакеты](https://docs.npmjs.com/about-public-packages/) +- [npm package.json: правила имени](https://docs.npmjs.com/cli/v11/configuring-npm/package-json/#name) - [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) - [npm publish: tarball и неизменяемость версии](https://docs.npmjs.com/cli/v11/commands/npm-publish/) - [GitHub Actions: закрепление SHA и обновления](https://docs.github.com/en/actions/reference/security/secure-use) From 1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 7 Sep 2026 13:07:18 +0400 Subject: [PATCH 6/9] feat: package exact solver as strict TypeScript library and standalone CLI Preserve the matrix reference, build four browser/module formats and a standalone CLI with Vite, add strict types and consumer tests, and prepare reproducible benchmark and release helpers. Workflow integration follows completed local verification. --- .gitattributes | 2 + .gitignore | 5 + .oxlintrc.json | 11 + AGENTS.md | 176 +- README.md | 328 +-- benchmarks/README.md | 245 +- benchmarks/catalog.mjs | 26 - benchmarks/catalog.ts | 61 + benchmarks/cli.ts | 107 + benchmarks/comparison.ts | 53 + benchmarks/harness.mjs | 403 --- benchmarks/harness.ts | 220 ++ benchmarks/memory.ts | 74 + benchmarks/reference-matrix/LICENSE | 201 ++ benchmarks/reference-matrix/manifest.json | 21 + .../reference-matrix/src}/cli.mjs | 0 .../reference-matrix/src}/index.mjs | 0 .../reference-matrix/src}/lock-definition.mjs | 0 .../reference-matrix/src}/matrix-analysis.mjs | 0 .../reference-matrix/src}/matrix-search.mjs | 0 .../reference-matrix/src}/result.mjs | 0 .../reference-matrix/src}/search-bfs.mjs | 0 .../reference-matrix/src}/search-limits.mjs | 0 .../reference-matrix/src}/solver.mjs | 0 .../reference-matrix/src}/state-codec.mjs | 0 .../reference-matrix/src}/transition.mjs | 0 benchmarks/rss.ts | 14 + benchmarks/snapshots/reference-matrix.json | 2394 +++++++++++++++++ benchmarks/solver.bench.ts | 24 + benchmarks/validation.mjs | 54 - benchmarks/validation.ts | 78 + cli/cli.ts | 102 + cli/main.ts | 4 + dist/gothic-lock-solver.js | 912 ------- docs/algorithm.md | 283 +- docs/api.md | 149 + docs/development.md | 126 + docs/releasing.md | 132 + .../plans/2026-09-07-typescript-library.md | 108 + .../2026-09-07-typescript-library-design.md | 53 +- package.json | 74 +- patches/vitest@5.0.0.patch | 32 + playwright.config.ts | 29 + pnpm-lock.yaml | 1482 ++++++++++ pnpm-workspace.yaml | 2 + scripts/benchmark-compare.ts | 2 + scripts/benchmark-memory-worker.ts | 25 + scripts/benchmark-memory.ts | 41 + scripts/benchmark-snapshot.ts | 29 + scripts/benchmark.mjs | 27 - scripts/benchmark.ts | 2 + scripts/browser/worker.mjs | 9 - scripts/build-browser.mjs | 116 - scripts/build.ts | 31 + scripts/ci-baseline.ts | 67 + scripts/release-browser.ts | 49 + scripts/release-files.ts | 118 + scripts/release-lib.ts | 106 + scripts/release-network.ts | 119 + scripts/release.ts | 96 + scripts/verify-package.ts | 82 + scripts/verify-web-core.mjs | 51 - solve-lock.mjs | 2 +- src/errors.ts | 24 + src/index.ts | 28 + src/indexed-heap.ts | 73 + src/indexed.ts | 24 + src/lock-model.ts | 58 + src/matrix-analysis.ts | 83 + src/matrix-search.ts | 140 + src/search-bfs.ts | 110 + src/search-limits.ts | 123 + src/types.ts | 16 + test/benchmark.test.mjs | 193 -- test/browser-bundle.test.mjs | 121 - test/browser-smoke.html | 40 - test/catalog.test.mjs | 45 - test/cli.test.mjs | 160 -- test/differential.test.mjs | 50 - test/e2e.test.mjs | 24 - test/lock-definition.test.mjs | 62 - test/matrix-search.test.mjs | 197 -- test/result.test.mjs | 54 - test/solver.test.mjs | 85 - test/state-codec.test.mjs | 23 - test/transition.test.mjs | 64 - test/web-core.test.mjs | 12 - tests/browser/catalog.test.ts | 33 + tests/distribution/artifacts.test.ts | 58 + tests/distribution/catalog.test.ts | 20 + tests/distribution/cli.test.ts | 39 + tests/distribution/fixtures/consumer.mjs | 44 + tests/distribution/fixtures/consumer.mts | 36 + .../distribution/fixtures/global-consumer.ts | 5 + tests/e2e/catalog.ts | 52 + tests/e2e/consumers.spec.ts | 195 ++ tests/e2e/fixtures/classic.html | 17 + tests/e2e/fixtures/classic.min.html | 17 + tests/e2e/fixtures/module.html | 18 + tests/e2e/fixtures/module.min.html | 18 + tests/e2e/fixtures/worker.html | 7 + tests/e2e/fixtures/worker.min.mjs | 5 + tests/e2e/fixtures/worker.mjs | 5 + tests/e2e/paths.ts | 10 + tests/e2e/server.ts | 75 + tests/helpers/oracle.ts | 55 + tests/types/assets.d.ts | 4 + tests/types/core-contract.ts | 37 + tests/unit/benchmark.test.ts | 155 ++ tests/unit/cli.test.ts | 85 + tests/unit/core-contract.test.ts | 102 + tests/unit/core-limits.test.ts | 45 + tests/unit/core-oracle.test.ts | 78 + tests/unit/release-files.test.ts | 103 + tests/unit/release-network.test.ts | 141 + tests/unit/release.test.ts | 66 + tsconfig.build.json | 14 + tsconfig.json | 29 + vite.config.ts | 31 + vitest.config.ts | 26 + 120 files changed, 9164 insertions(+), 3227 deletions(-) create mode 100644 .gitattributes create mode 100644 .oxlintrc.json delete mode 100644 benchmarks/catalog.mjs create mode 100644 benchmarks/catalog.ts create mode 100644 benchmarks/cli.ts create mode 100644 benchmarks/comparison.ts delete mode 100644 benchmarks/harness.mjs create mode 100644 benchmarks/harness.ts create mode 100644 benchmarks/memory.ts create mode 100644 benchmarks/reference-matrix/LICENSE create mode 100644 benchmarks/reference-matrix/manifest.json rename {src => benchmarks/reference-matrix/src}/cli.mjs (100%) rename {src => benchmarks/reference-matrix/src}/index.mjs (100%) rename {src => benchmarks/reference-matrix/src}/lock-definition.mjs (100%) rename {src => benchmarks/reference-matrix/src}/matrix-analysis.mjs (100%) rename {src => benchmarks/reference-matrix/src}/matrix-search.mjs (100%) rename {src => benchmarks/reference-matrix/src}/result.mjs (100%) rename {src => benchmarks/reference-matrix/src}/search-bfs.mjs (100%) rename {src => benchmarks/reference-matrix/src}/search-limits.mjs (100%) rename {src => benchmarks/reference-matrix/src}/solver.mjs (100%) rename {src => benchmarks/reference-matrix/src}/state-codec.mjs (100%) rename {src => benchmarks/reference-matrix/src}/transition.mjs (100%) create mode 100644 benchmarks/rss.ts create mode 100644 benchmarks/snapshots/reference-matrix.json create mode 100644 benchmarks/solver.bench.ts delete mode 100644 benchmarks/validation.mjs create mode 100644 benchmarks/validation.ts create mode 100644 cli/cli.ts create mode 100644 cli/main.ts delete mode 100644 dist/gothic-lock-solver.js create mode 100644 docs/api.md create mode 100644 docs/development.md create mode 100644 docs/releasing.md create mode 100644 docs/superpowers/plans/2026-09-07-typescript-library.md create mode 100644 patches/vitest@5.0.0.patch create mode 100644 playwright.config.ts create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 scripts/benchmark-compare.ts create mode 100644 scripts/benchmark-memory-worker.ts create mode 100644 scripts/benchmark-memory.ts create mode 100644 scripts/benchmark-snapshot.ts delete mode 100644 scripts/benchmark.mjs create mode 100644 scripts/benchmark.ts delete mode 100644 scripts/browser/worker.mjs delete mode 100644 scripts/build-browser.mjs create mode 100644 scripts/build.ts create mode 100644 scripts/ci-baseline.ts create mode 100644 scripts/release-browser.ts create mode 100644 scripts/release-files.ts create mode 100644 scripts/release-lib.ts create mode 100644 scripts/release-network.ts create mode 100644 scripts/release.ts create mode 100644 scripts/verify-package.ts delete mode 100644 scripts/verify-web-core.mjs create mode 100644 src/errors.ts create mode 100644 src/index.ts create mode 100644 src/indexed-heap.ts create mode 100644 src/indexed.ts create mode 100644 src/lock-model.ts create mode 100644 src/matrix-analysis.ts create mode 100644 src/matrix-search.ts create mode 100644 src/search-bfs.ts create mode 100644 src/search-limits.ts create mode 100644 src/types.ts delete mode 100644 test/benchmark.test.mjs delete mode 100644 test/browser-bundle.test.mjs delete mode 100644 test/browser-smoke.html delete mode 100644 test/catalog.test.mjs delete mode 100644 test/cli.test.mjs delete mode 100644 test/differential.test.mjs delete mode 100644 test/e2e.test.mjs delete mode 100644 test/lock-definition.test.mjs delete mode 100644 test/matrix-search.test.mjs delete mode 100644 test/result.test.mjs delete mode 100644 test/solver.test.mjs delete mode 100644 test/state-codec.test.mjs delete mode 100644 test/transition.test.mjs delete mode 100644 test/web-core.test.mjs create mode 100644 tests/browser/catalog.test.ts create mode 100644 tests/distribution/artifacts.test.ts create mode 100644 tests/distribution/catalog.test.ts create mode 100644 tests/distribution/cli.test.ts create mode 100644 tests/distribution/fixtures/consumer.mjs create mode 100644 tests/distribution/fixtures/consumer.mts create mode 100644 tests/distribution/fixtures/global-consumer.ts create mode 100644 tests/e2e/catalog.ts create mode 100644 tests/e2e/consumers.spec.ts create mode 100644 tests/e2e/fixtures/classic.html create mode 100644 tests/e2e/fixtures/classic.min.html create mode 100644 tests/e2e/fixtures/module.html create mode 100644 tests/e2e/fixtures/module.min.html create mode 100644 tests/e2e/fixtures/worker.html create mode 100644 tests/e2e/fixtures/worker.min.mjs create mode 100644 tests/e2e/fixtures/worker.mjs create mode 100644 tests/e2e/paths.ts create mode 100644 tests/e2e/server.ts create mode 100644 tests/helpers/oracle.ts create mode 100644 tests/types/assets.d.ts create mode 100644 tests/types/core-contract.ts create mode 100644 tests/unit/benchmark.test.ts create mode 100644 tests/unit/cli.test.ts create mode 100644 tests/unit/core-contract.test.ts create mode 100644 tests/unit/core-limits.test.ts create mode 100644 tests/unit/core-oracle.test.ts create mode 100644 tests/unit/release-files.test.ts create mode 100644 tests/unit/release-network.test.ts create mode 100644 tests/unit/release.test.ts create mode 100644 tsconfig.build.json create mode 100644 tsconfig.json create mode 100644 vite.config.ts create mode 100644 vitest.config.ts diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2dab4a1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Unified diff context uses a leading space before the original tab indentation. +patches/*.patch whitespace=-space-before-tab diff --git a/.gitignore b/.gitignore index 4d5b0c2..328bcac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ node_modules/ coverage/ +dist/ +artifacts/ +playwright-report/ +test-results/ +*.cpuprofile *.tmp project_sources/ upload/ diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..38e255e --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,11 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "unicorn", "oxc"], + "categories": { "correctness": "error" }, + "rules": { + "typescript/no-explicit-any": "error", + "typescript/no-non-null-assertion": "error", + "typescript/ban-ts-comment": ["error", { "ts-ignore": true, "ts-nocheck": true, "ts-expect-error": "allow-with-description" }] + }, + "ignorePatterns": ["**/*.mjs", "**/*.js", "dist/**", "node_modules/**", "tests/distribution/fixtures/**"] +} diff --git a/AGENTS.md b/AGENTS.md index 94c21e9..ea6a909 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,90 +1,92 @@ -# AGENTS.md - -Этот файл — основная точка входа для coding-agent, работающего с репозиторием. - -## Назначение проекта - -`gothic-lock-solver` вычисляет кратчайшую последовательность команд для пластинчатых замков **Gothic 1 Remake**. Текущее приложение — CLI на Node.js; вычислительное ядро подготовлено для повторного использования в будущем сайте. - -## Что прочитать перед изменениями - -1. [README.md](README.md) — пользовательский интерфейс и примеры. -2. [Спецификацию](docs/superpowers/specs/2026-08-31-gothic-lock-solver.md) — источник требований к механике. -3. [Спецификацию матричного поиска](docs/superpowers/specs/2026-09-07-matrix-search.md) — обновлённый контракт алгоритма и ресурсов. -4. [Выполненный план](docs/superpowers/plans/2026-08-31-gothic-lock-solver.md) — архитектурные решения и история реализации. -5. Тесты изменяемого модуля. - -Если README, план или код расходятся со спецификацией, приоритет имеет спецификация и подтверждённое тестами поведение. - -## Архитектура - -- `solve-lock.mjs` — исполняемая точка входа CLI. -- `src/index.mjs` — стабильный facade для CLI и будущего сайта. -- `src/lock-definition.mjs` — разбор и валидация модели замка. -- `src/state-codec.mjs` — base-7 кодирование состояний. -- `src/transition.mjs` — применение команд и генерация переходов. -- `src/solver.mjs`, `src/search-bfs.mjs` — точный BFS по действиям. -- `src/matrix-analysis.mjs`, `src/matrix-search.mjs` — точная матричная подготовка и A*. -- `src/search-limits.mjs` — отдельные ограничения вычисления. -- `benchmarks/` — 45 конфигураций, независимая симуляция и закреплённый исходный BFS. -- `src/result.mjs` — результат, метрики и консольное представление. -- `src/cli.mjs` — аргументы, файлы и коды завершения. -- `test/` — модульные, дифференциальные и интеграционные тесты. -- `.codex/skills/gothic-lock-solver/` — workflow интерактивного решения замка. - -## Неизменяемые правила модели - -- Модель: не менее 2 пластин; позиции каждой — целые числа от `1` до `7`. Ресурсные пределы решателя не являются ограничением игровой механики. -- Цель — позиция `4` у каждой пластины. -- Влево означает `+1`, вправо — `-1`. -- Матрица связей направленная; значения только `-1`, `0`, `1`, диагональ равна `0`. -- Движение выбранной пластины учитывается отдельно от матрицы. -- Связи применяются один раз и не каскадируют. -- Если хотя бы одна затронутая пластина выходит за границы, вся команда блокируется без изменения состояния. -- Одна команда может сдвигать пластину на `1..6` делений и имеет стоимость `1` для поиска. -- Главная гарантия обоих режимов — минимум действий. Матричный A* используется по умолчанию; среди равных решений он детерминирован, но не обязан повторять путь BFS. -- В режиме `bfs` сохраняется прежний порядок: меньшая пластина, `left` перед `right`, большее `steps` перед меньшим. -- Минимум разных выбранных пластин и единичных сдвигов не является общей гарантией. -- Исчерпание ресурсов обозначается `SearchLimitError`; оно не означает нерешаемость. - -Не изменяй эти правила без явного изменения спецификации и новых тестов. - -## Правила разработки - -- Node.js 20+, JavaScript ESM (`.mjs`), без сторонних runtime-зависимостей. -- Делай минимальные изменения и сохраняй существующие публичные интерфейсы. -- Не связывай вычислительное ядро с файловой системой, CLI или UI. -- Для сайта используй `solveLock` из `src/index.mjs`, а не копию алгоритма. -- Изменение поведения начинай с теста, воспроизводящего требуемый результат или дефект. -- Не добавляй `project_sources/`, `upload/`, PDF, временные файлы и секреты в Git. -- Не редактируй `.env` и не добавляй новые зависимости без явной необходимости. -- Пользовательские сообщения CLI остаются на русском; JSON-поля и значения направления остаются совместимыми с текущим API. - -## Команды - -```bash -# Полная проверка +# Agent instructions + +## Purpose and authority + +Gothic Lock Solver is a strict TypeScript mathematical library and standalone +Node.js CLI. The active requirements are in +[the library specification](docs/superpowers/specs/2026-09-07-typescript-library-design.md). +The owner approved a migration from the preserved JavaScript reference; old +specifications and reports are historical, not the active module contract. + +Read [README](README.md), [API](docs/api.md), [algorithm](docs/algorithm.md), +[development](docs/development.md) and relevant tests before changing code. +Public documentation must be self-contained; access to private owner notes is +not a development prerequisite. Do not copy private notes into this repository. + +## Invariants + +- At least two plates; numeric pin positions 1–7; goal 4. +- `links[source][target]` has −1/0/+1 entries and zero diagonal. +- Direct links apply once; no cascading; the selected plate's effect is implicit. +- A legal command keeps every affected pin in range throughout the movement. +- Module commands are zero-based `[index, delta]`, nonzero delta −6…+6. +- `solveLock(state, links)` has exactly two inputs; no default export. +- Successful solutions minimize grouped actions. Distinct controls and individual + shifts are separate reported metrics, not additional global guarantees. +- `null` means proved unreachable. Resource exhaustion throws `SearchLimitError`. +- Inputs are not mutated. Runtime validation and exact integer/rational arithmetic + cannot be replaced by assertions, coercions or floating-point tolerances. + +## Architecture + +- `src/index.ts`: public facade and explicit type/error exports. +- `src/lock-model.ts`: validated immutable input; single owner of input invariants. +- `src/matrix-analysis.ts`: exact rational Gauss–Jordan analysis. +- `src/matrix-search.ts`: certificate and exact A*. +- `src/search-bfs.ts`: exact singular-case fallback. +- `src/search-limits.ts`: prepared effects, encoding and computation budgets. +- `src/indexed-heap.ts`: priority queue; `src/indexed.ts`: checked indexed access. +- `cli/`: Node-only adapter preserving historical console/JSON conventions. +- `benchmarks/`: pinned fixtures, immutable references, independent replay and reports. +- `scripts/`: Vite build, consumer verification, benchmarks and release orchestration. +- `tests/`: behavior, exhaustive oracle, static type, distribution and real browser checks. + +Use composition and small domain objects with clear invariants. Keep exact +arithmetic as functions where a class adds no state or responsibility. Avoid +frameworks, duplicate validation policies, abstraction registries and public +options without a demonstrated requirement. Apply SOLID, KISS, YAGNI and DRY +proportionately. Test observable behavior, not private implementation inventories. + +## Changes and verification + +Start behavioral changes with a failing test and record the relevant red/green +evidence. Use pnpm and the exact current dependencies in the lockfile. Keep strict +TypeScript checks and lint warnings-as-errors. No `any`, `@ts-ignore`, non-null +assertions or unjustified type assertions; `@ts-expect-error` is restricted to +negative type-contract tests. Use current official tool documentation when an API +changes. Do not disable library checking to hide dependency declaration defects. + +Local gates precede workflow edits: + +```sh +pnpm install --frozen-lockfile pnpm test - -# Пример запуска -node solve-lock.mjs examples/lock.example.json - -# JSON-результат -node solve-lock.mjs examples/lock.example.json --output solution.json - -# Бенчмарк 45 замков и отчёт -pnpm benchmark --output-dir docs/benchmarks - -# Граница браузерного ESM-ядра -pnpm verify:web-core +pnpm test:coverage +pnpm benchmark --reference ../unlockmyloot --output-dir artifacts/benchmark ``` -## Критерии готовности - -- Все тесты проходят без пропусков. -- Все 45 конфигураций проверены независимой симуляцией и точным минимумом действий. -- Отчёт измеряет скорость в миллисекундах; unit tests не имеют нестабильных временных порогов. -- Новое поведение покрыто тестами. -- Пример из README соответствует фактическому выводу. -- Спецификация и публичная документация обновлены при изменении интерфейса. -- В diff нет PDF, секретов, временных файлов и несвязанных изменений. +Core and shipped CLI coverage must be at least 80% for every coverage metric. +Every one of the 45 locks must replay legally and retain minimum action counts. +The independent small-state oracle and all required real browser projects must +pass. Verify the built files, isolated CLI and clean installed tarball; source-only +or VM tests do not establish browser/distribution compatibility. + +Compare performance against the PR target in the same environment, preserving +raw samples and noise calibration. Never lower expected action counts or update +snapshots merely to turn a regression green. An inconclusive timing result stays +inconclusive. Record exact revisions, artifact hashes and environment details. + +An independent reviewer checks the final implementation, tests, mathematical +claims and owner principles before the draft PR is declared ready for review. +The reference branch remains unchanged. Do not merge main, publish packages or +modify the upstream project as part of this migration. + +## Repository hygiene + +Keep README and current engineering docs in English; preserve Russian CLI output. +Use the same focused docs from README and this file instead of duplicating a wiki. +Never commit secrets, uploaded PDFs, `project_sources/`, `upload/`, dependency +folders or generated build/test artifacts. Do not edit `.env` files. Historical +benchmark reports and pinned reference sources are intentionally versioned. +Use official vendor Actions pinned to verified full SHAs, official npm/gh CLIs, +and standard free public runners. No separate CDN deployment service is needed. diff --git a/README.md b/README.md index eeb7afb..43a085c 100644 --- a/README.md +++ b/README.md @@ -1,231 +1,173 @@ # Gothic Lock Solver -Решатель пластинчатых замков из **Gothic 1 Remake**. По начальному состоянию и направленной матрице зависимостей программа находит кратчайшую последовательность команд для установки всех пластин в позицию `4`. +An exact **minimum-action** solver for the coupled plate locks in Gothic 1 Remake. +A small TypeScript library for browsers, Web Workers and Node.js, with a standalone +CLI. It has no runtime dependencies. -## Возможности +One action selects a plate and moves it several positions in one direction. +The solver minimizes these grouped actions, rather than individual clicks. +It combines exact matrix analysis, an optimality certificate, A* and a BFS fallback. +[The algorithm and its proof](docs/algorithm.md) explain the guarantee and its limits. -- от 2 пластин, по 7 позиций у каждой; вычислительные ресурсы ограничены отдельно; -- направленные синхронные и реверсивные зависимости; -- атомарная блокировка недопустимого хода; -- точный матричный анализ и A* с гарантией минимального количества команд; -- точный BFS для вырожденных матриц и сравнений; -- 45 каталожных конфигураций, независимые проверки и воспроизводимый бенчмарк; -- вывод в консоль или JSON-файл; -- отсутствие сторонних зависимостей. +> Publication status: this branch prepares the first public release. The package +> and CDN examples below become available after the first verified npm publication. +> Replace `VERSION` with an exact published version; these are integration templates. -## Требования +## Install and import -- Node.js 20 или новее. - -Устанавливать пакеты не требуется. - -## Формат входного файла - -```json -{ - "state": [1, 7, 4, 2, 6], - "links": [ - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0] - ] -} +```sh +npm install gothic-lock-solver ``` -- `state` — текущие позиции пластин от `1` до `7`; -- `links[i][j] = 1` — пластина `j` движется вместе с выбранной пластиной `i`; -- `links[i][j] = -1` — пластина `j` движется в противоположном направлении; -- `links[i][j] = 0` — зависимости нет; -- диагональ матрицы всегда равна `0`. +```ts +import { solveLock } from "gothic-lock-solver"; -Влево увеличивает позицию на `1`, вправо уменьшает её на `1`. Цель — состояние `[4, 4, ...]`. - -## Пример использования - -```bash -node solve-lock.mjs examples/lock.example.json +const commands = solveLock([6, 2], [[0, -1], [0, 0]]); +// [[0, -2]]: decrease pin 0 by two; its reverse link increases pin 1 by two. ``` -Ответ в консоли: +A namespace import is also supported: -```text -Найдено команд: 4 -1. Пластина 1 - влево x3 -2. Пластина 2 - вправо x3 -3. Пластина 4 - влево x2 -4. Пластина 5 - вправо x2 -``` +```ts +import * as GothicLockSolver from "gothic-lock-solver"; -Для сохранения результата в JSON: - -```bash -node solve-lock.mjs examples/lock.example.json --output solution.json +const commands = GothicLockSolver.solveLock([6, 2], [[0, -1], [0, 0]]); ``` -Пример `solution.json`: +There is no default export. Type declarations ship with the package. +[The complete contract](docs/api.md) covers tuple inference, validation and errors. -```json -{ - "status": "solved", - "initialState": [1, 7, 4, 2, 6], - "targetState": [4, 4, 4, 4, 4], - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - } - ], - "finalState": [4, 4, 4, 4, 4], - "metrics": { - "commands": 4, - "divisions": 10, - "plateSwitches": 3 - } -} -``` - -## Проверка - -```bash -pnpm test -``` - -Подробная [спецификация механики](docs/superpowers/specs/2026-08-31-gothic-lock-solver.md) -и [обновлённый контракт матричного поиска](docs/superpowers/specs/2026-09-07-matrix-search.md) -находятся в `docs/superpowers`. +## Use directly in HTML -## Вычислительное ядро +No build step, package installation or CDN account is required by the integrator. +The npm package already contains the exact files served by jsDelivr. -```js -import { solveLock, SearchLimitError } from './src/index.mjs'; - -const definition = { state: [1, 7], links: [[0, 0], [0, 0]] }; -const result = solveLock(definition); // matrix-astar по умолчанию -const comparison = solveLock(definition, { algorithm: 'bfs' }); +```html + + ``` -Одна команда сдвигает выбранную пластину на `steps` делений и стоит одно действие. -Оба алгоритма гарантируют минимум действий. Их конкретные последовательности могут -отличаться. В режиме BFS сохранён прежний порядок выбора: меньший номер пластины, -`left` перед `right`, большее `steps` перед меньшим. Матричный режим детерминирован, -но не гарантирует глобальный минимум разных пластин или единичных сдвигов среди -решений с равным количеством действий. - -Начальная матрица задаёт необходимый суммарный баланс сдвигов. A* использует его -как нижнюю оценку оставшихся действий и проверяет границы каждого перехода. -Наличие целочисленного решения уравнения само по себе не доказывает возможность -открыть замок. [Модель и доказательство эвристики](docs/algorithm.md). - -`SearchLimitError` означает, что вычисление не укладывается в заданный бюджет или -представление состояния. Это не результат `unsolvable`. CLI печатает такую ошибку -на русском и завершается с кодом `1`. Ограничения механики не следует путать -с возможностями конкретного запуска; стартовая граница будущего интерфейса 8 -не требует жёсткого ограничения модели числом 8. - -Бюджеты задаются в том же объекте `options`: `maxVisited` (2 000 000), -`maxExpanded` (1 000 000), `maxFrontier` (1 000 000), `maxDenseBytes` (64 MiB). -Последний параметр выбирает плотное или разреженное хранение BFS и не ограничивает -всю память процесса. [Подробности и сложность](docs/algorithm.md). - -## Использование в браузере - -Ядро — обычные ES-модули без Node.js API, сети, DOM и сторонних runtime-зависимостей. -Файлы `src/` можно хранить внутри приложения и импортировать с того же origin: +For browser modules: ```html ``` -Публикация в npm, внешнее CDN и наш сервер не нужны. Для потенциально тяжёлых -входов вызывайте синхронный решатель в модульном Web Worker; рабочий пример — -[`scripts/browser/worker.mjs`](scripts/browser/worker.mjs). Передавайте ему -`{state, links}` через `postMessage`; он возвращает результат или имя и текст ошибки. +Pin the full version and file path. The library is synchronous; use a +[Web Worker](docs/api.md#web-workers) for an interface that must remain responsive +while solving a difficult lock. -Для страницы без ESM есть готовый файл [`dist/gothic-lock-solver.js`](dist/gothic-lock-solver.js): +## Contract at a glance -```html - - +```ts +solveLock(state, links): readonly Command[] | null +// Command = readonly [index: number, delta: -6 | ... | -1 | 1 | ... | 6] ``` -Скопируйте этот файл рядом с HTML. У принимающего проекта не появятся зависимости -или этап сборки. Файл не загружает код по сети и не требует ESM-сервера; ограничения -запуска конкретного браузера через `file://` следует проверять отдельно. -В нашем репозитории `pnpm build:browser` воспроизводит файл из текущих исходников, -а `pnpm verify:browser-bundle` проверяет, что он не устарел. Скрипт сборки поддерживает -только явно разрешённую структуру модулей этого ядра и отвергает неподдерживаемый -синтаксис импортов и экспортов. - -```bash -pnpm verify:web-core -python3 -m http.server 8000 +| Value | Meaning | +| --- | --- | +| `state[i]` | Pin position, an integer from 1 to 7; the goal is 4 | +| `state.length` | Number of plates, at least 2 | +| `links[source][target]` | Direct influence: −1 reverse, 0 none, +1 same direction | +| Diagonal | Always 0; the selected plate's own movement is implicit | +| `[index, delta]` | Zero-based plate index and signed change in numeric pin position | +| `[]` | Already open | +| `null` | The target was proved unreachable | +| `LockInputError` | Invalid values or dimensions | +| `SearchLimitError` | A computation limit was reached; this does not prove impossibility | + +Links do not cascade. Every affected pin must stay in positions 1–7 throughout +an action. Inputs are copied and never mutated. Positive delta means a higher +**pin position**; map it to physical left/right in the consuming UI. + +The model does not impose an eight-plate maximum. The current state encoding +supports up to 18 plates, with additional search budgets. Runtime and memory can +grow exponentially; successful results retain the exact minimum-action guarantee. +The solver does not promise minimum individual clicks or minimum distinct plates +among equally short action sequences. + +## Choose a distribution file + +| File in `dist/` | Use | +| --- | --- | +| `gothic-lock-solver.mjs` | Readable ESM, Node.js or browser import | +| `gothic-lock-solver.min.mjs` | Minified ESM | +| `gothic-lock-solver.js` | Readable classic browser script, global `GothicLockSolver` | +| `gothic-lock-solver.min.js` | Minified classic browser script | +| `gothic-lock-solver.cli.mjs` | Standalone Node.js command-line tool | + +Each runtime file is self-contained. The CLI uses only built-in Node.js modules; +the four core files use no Node.js or DOM APIs, additional chunks or network calls. +The classic files are IIFEs, not CommonJS modules. Declarations, license and build +checksums accompany the runtime files. + +## Run the CLI + +Use a maintained Node.js 22, 24 or 26 release (latest patch recommended). After installing the package, run `gothic-lock-solver`. +Alternatively, download the CLI release asset and run it directly: + +```sh +node gothic-lock-solver.cli.mjs lock.json --output solution.json ``` -Первая команда загружает настоящий граф ESM-зависимостей в изолированный JS-контекст -без `process`, `Buffer` и `require`. Это проверка границы зависимостей, не эмуляция -браузера. Для проверки в настоящем браузере откройте -`http://localhost:8000/test/browser-smoke.html`: страница проверяет импорт и решение -восьмипластинного замка на главном потоке и в модульном Worker. -HTTP-сервер нужен для ESM; открытие этой страницы через `file://` не поддерживается. - -## Бенчмарк и доказательный отчёт - -```bash -pnpm benchmark --output-dir docs/benchmarks +```json +{ + "state": [6, 2], + "links": [[0, -1], [0, 0]] +} ``` -Запуск проверяет все 45 входов, прогревает реализации, делает повторные измерения -и создаёт подкаталог `run-*` с JSON и Markdown. Без внешнего checkout сравниваются -матричный режим, текущий BFS и закреплённый исходный BFS нашего проекта. -Исторические показатели UnlockMyLoot помечаются отдельно и не выдают себя -за свежие измерения скорости. - -Чтобы сравнить сторонний решатель в том же JS-runtime: - -```bash -git clone https://github.com/1h8s/unlockmyloot.git ../unlockmyloot -git -C ../unlockmyloot checkout eee0bf50ebcb7fffd2b47954fd76bb015854e365 -pnpm benchmark --reference ../unlockmyloot --repetitions 5 --warmups 1 --output-dir docs/benchmarks +The CLI retains the original Russian console output and JSON result format. +Its compatibility adapter uses **one-based plates**, `left` for increasing pin +position and `right` for decreasing it. The module API uses zero-based tuples. +Exit codes are `0` for solved, `2` for proved unreachable and `1` for input, +filesystem or resource errors. No output file is written unless `--output` is +supplied; an existing output file is replaced. See [CLI details](docs/api.md#cli). + +## Evidence and development + +The test corpus contains all **45 pinned catalog locks**, plus all 441 two-plate +configurations and 192 reproducible three-plate configurations. An independent +unit-step simulator checks every returned path; a separate exhaustive oracle +checks minimum actions on small models. + +Validation covers Node.js, real Chromium/Firefox/WebKit, both module bundles, +both classic bundles, Workers, the installed npm tarball and the standalone CLI. +Production core and CLI coverage must be at least **80% each for statements, +branches, functions and lines**. Type checking uses strict TypeScript 7; lint +warnings are errors. + +[Benchmark documentation](benchmarks/README.md) describes the full reports, +immutable fixtures, paired timing, peak RSS and baseline comparisons. +Actions, distinct selected plates and individual shifts are reported separately. +A timing result is `passed`, `regression` or `inconclusive`; noisy measurements +are not silently declared successful. Machine-specific timings are evidence for +that environment, not a universal latency promise. + +```sh +pnpm install --frozen-lockfile +pnpm test +pnpm test:coverage +pnpm benchmark --reference ../unlockmyloot --output-dir artifacts/benchmark ``` -Проверяются закреплённый commit и хэши исходников. Чужая функция исполняется без -изменения алгоритма; её код не включён в этот репозиторий. Происхождение данных -каталога сохраняется в `benchmarks/fixtures`. - -Каждый найденный путь воспроизводится независимой симуляцией по одному делению. -Минимальность количества действий проверяется точным BFS. Отчёт показывает -действия, разные выбранные пластины, единичные сдвиги, время в **миллисекундах**, -среду и хэши. Обычные тесты не требуют «быстрее 1 мс»: скорость зависит от машины. -Для быстрой проверки самого сценария есть `--smoke`; её неполный охват явно указан. - -Пример сохранённого полного запуска: [docs/benchmarks](docs/benchmarks). -Порядок предложения необязательного режима стороннему проекту описан в -[плане вклада в UnlockMyLoot](docs/upstream-contribution.md). +Read [development and architecture](docs/development.md), +[algorithm and complexity](docs/algorithm.md), [release process](docs/releasing.md), +and [agent instructions](AGENTS.md) before contributing. Local verification +precedes workflow integration. The reference implementation remains available in +`feat/matrix-astar-benchmarks` and as a hash-pinned benchmark reference. -## Лицензия +## Attribution and license -[Apache License 2.0](LICENSE) +Apache-2.0; see [LICENSE](LICENSE). Catalog provenance and hashes are recorded in +[the fixture manifest](benchmarks/fixtures/manifest.json). +[Unlock My Loot](https://unlockmyloot.com/) and its +[source repository](https://github.com/1h8s/unlockmyloot) provide the catalog and +comparison implementation. Its solver is measured from a separately checked-out, +pinned upstream revision and is not bundled or republished in this package. diff --git a/benchmarks/README.md b/benchmarks/README.md index 52945e2..9164ab4 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -1,59 +1,194 @@ -# Reproducible catalog evidence - -The 45 fixtures in `fixtures/catalog.json` are fixed catalog configurations from -[UnlockMyLoot](https://github.com/1h8s/unlockmyloot/tree/eee0bf50ebcb7fffd2b47954fd76bb015854e365), -commit `eee0bf50ebcb7fffd2b47954fd76bb015854e365`. Each entry retains its name, -encoded lock code, source URL, initial state, directed links, and original -catalog/research provenance. These are factual configurations and measured paths; -no UnlockMyLoot solver source is included. `fixtures/manifest.json` records source -file hashes, the imported analysis checksums, and the fixture checksum. - -`expectedActions` is the exact grouped-command BFS minimum previously established -by the original Apache BFS and cross-checked with optimized BFS and the matrix -prototype. `baselineCommands` preserves that BFS's deterministic path. Both -public matrix search and current exact BFS must reproduce the minimum; BFS must -also preserve the path. The independent unit-click verifier in `validation.mjs` -does not use the production transitions or state encoder. - -`historicalReference` preserves the original reference path, A/U/C, switches, -and prior timing observations. These timings are marked historical and excluded -from live comparisons. A = grouped actions, U = distinct selected plates, -C = unit divisions of selected plates. A is the solver's optimization objective; -U and C are descriptive and need not be minimal. +# Reproducible benchmark evidence + +The 45 fixed catalog inputs in `fixtures/catalog.json` come from +[UnlockMyLoot at eee0bf50ebcb7fffd2b47954fd76bb015854e365](https://github.com/1h8s/unlockmyloot/tree/eee0bf50ebcb7fffd2b47954fd76bb015854e365). +The original fixture bytes, IDs, initial positions, directed links, provenance, +encoded lock codes, optimum grouped-action counts and historic paths are unchanged. +The catalog SHA256 is +`0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675`. +The loader verifies this immutable hash independently of the manifest. + +`baseline/` preserves the original Apache-2.0 BFS unchanged at +`a87e739a22ccc4215d6d9b14fa06b0738171cb60`. `reference-matrix/` preserves the +byte-identical Apache matrix implementation measured at +`6b1cfbc13bcec68f609d45d5dc97504dd84f1760`, including its original license. +Their manifests verify every preserved source file. These are benchmark references; +they are excluded from the published library. The external AGPL solver is never +vendored: `--reference` reads the separate pinned checkout, verifies its Git revision, +whole `index.html` hash and marked solver-block hash, then evaluates that unchanged +block in the same Node realm as the other timed implementations. + +## Running the controlled comparison ```bash -node scripts/benchmark.mjs --repetitions 5 --warmups 1 --output-dir docs/benchmarks -node scripts/benchmark.mjs --repetitions 5 --warmups 1 --output-dir docs/benchmarks --reference /path/to/unlockmyloot -node scripts/benchmark.mjs --smoke --repetitions 1 --warmups 0 --output-dir /tmp/lock-smoke +pnpm build +pnpm benchmark --reference /path/to/unlockmyloot --output-dir docs/benchmarks + +pnpm benchmark:compare \ + --baseline-module /path/to/base/dist/gothic-lock-solver.mjs \ + --baseline-snapshot /path/to/base-quality.json \ + --baseline-format tuple \ + --reference /path/to/unlockmyloot \ + --output-dir docs/benchmarks ``` -The default benchmarks all 45 inputs. `--smoke` selects the first two and labels -the scope `subset`; it is not evidence for the full catalog. Positive integer -repetitions and nonnegative integer warmups are required. Runtime dependencies, -network access, and a reference checkout are not required for ordinary tests. - -The optional reference requires the pinned Git HEAD, exact `index.html` hash, -and exact marked solver-block hash. The unchanged block is compiled using -`Function` in the same Node.js realm as the other solvers. Reference click -grouping is outside timing; current and original Apache BFS are timed through -their public facades, including input validation and result construction. - -Each measured and warmup solution passes independent replay, action-count, -determinism, input-mutation and metric checks. Current BFS and original BFS must -match the pinned path. Live reference output must match its pinned observed -path and A/U/C. The reference has a different optimization objective, so it need -not attain the exact minimum grouped-action count. - -Every successful invocation creates a new `run-*` directory containing -`benchmark.json` and `benchmark.md`. Both artifacts are rendered after all -correctness checks; a single directory rename publishes them together. Existing -reports are never overwritten. Failed correctness checks publish no report. -Reports contain raw timing samples, per-lock min/median/max milliseconds, paths, -aggregates, source/core/fixture hashes, revisions, runtime and CPU metadata, and -measured matrix wins/ties/losses. No unit test imposes a wall-clock threshold. -Measurements run in Node.js and do not measure actual browser or Web Worker -performance. The harness uses natural garbage collection and never forces GC. - -`runBenchmark` and `publishReport` are separate exported helpers so focused tests -can inject tiny deterministic solvers and clocks to exercise evidence failures -without timing 45 slow baseline calls. +`benchmark` defaults to the pinned matrix reference. `benchmark:compare` requires +explicit module and snapshot paths from the PR target branch or previous release. +Both default to the candidate's built `dist/gothic-lock-solver.mjs`; the optional +`--candidate-module PATH` selects another named-export ESM. Its exact bytes and +SHA256 appear in evidence. `--baseline-format legacy` adapts the old one-argument +facade: one-based `plate` becomes a zero-based index, `left` becomes positive delta, +and `right` negative delta. `tuple` calls `solveLock(state, links)` directly. + +The default uses two warmups and seven measured repetitions per implementation +and lock. `--warmups N`, `--repetitions N` and `--memory-repetitions N` explicitly +set sampling. At least five matched measurements and five unchanged-control +measurements are needed for a performance verdict. Positive repetition counts +and nonnegative warmups are validated; duplicate and unknown arguments fail. + +`--smoke` selects the first two locks and labels the timing report `subset`. +`--skip-memory` explicitly records memory as unavailable and cannot establish a +performance pass. Neither is complete release evidence. Without `--reference`, +upstream cached counts remain visible, but there are no live upstream timings. + +## Quality and performance gates + +`validation.ts` independently replays tuple commands one unit division at a time. +It imports no production transition, state encoder, matrix analysis or search. +Every warmup and timed result must reach the goal, preserve input data and repeat +its deterministic path. Both candidate and baseline must attain the exact A +minimum originally proven by BFS on every fixture. Original BFS paths must match +the preserved path; live upstream paths and metrics must match pinned observations. + +Metrics are kept separately for each implementation and each lock: + +| Metric | Meaning | Comparison rule | +| --- | --- | --- | +| A | Grouped commands | Must equal the fixture's proven optimum | +| U | Distinct selected plates | An increase over the target snapshot fails quality | +| C | Sum of absolute selected-plate deltas | An increase over the target snapshot fails quality | +| plateSwitches | Changes of selected plate between adjacent commands | An increase fails this migration's quality gate | + +Only A is the solver's optimization objective. U/C/switches are descriptive; the +migration still must preserve them unless a separate reviewed change explicitly +accepts different behavior. Improving one metric does not conceal deterioration +in another. A bad path, lost solvability, input mutation, resource failure or +invalid command is a deterministic failure independent of timing uncertainty. + +The live baseline must reproduce the supplied target snapshot, including commands. +Candidate expectations are never consulted. Snapshot source module SHA256 must +match the actual loaded baseline. Thus editing the candidate's snapshot cannot +mask a regression against the PR target. Historic timing values are provenance +only and never establish a current performance pass. + +Timing calls share one Node process. The harness calibrates the unchanged baseline +against itself, then rotates measured implementation order by lock and round. +The public call is inside the timer; input cloning, normalization, independent +replay, metric calculation and reporting are outside it. Public validation and +legacy result construction are part of their respective public call costs. +Raw sample order, min, median, nearest-rank p95 and max are retained. + +The performance procedure uses paired log ratios and a conservative uncertainty +envelope (`mean ± 2.776 × standard error`, at least five observations). This is a +practical noise screen, not a claim of an exact distribution-free confidence +interval. The unchanged-control envelope determines the tolerance. A control +envelope wider than 25% cannot certify equivalence; it can still identify a +slowdown clearly exceeding that envelope. This ceiling rejects noisy evidence; +it is not a regression allowance. + +- `passed`: the candidate's upper slowdown envelope lies within the calibrated + control envelope. +- `regression`: a slowdown exceeds the envelope and is confirmed in one bounded + repeat. +- `inconclusive`: insufficient samples, wide control noise, overlap or conflicting + initial/repeat observations. This does not mean that regression is absent. + +A non-pass receives one bounded repeat; both attempts and raw samples remain in +JSON. An initial regression followed by a pass remains inconclusive. No repeated +runs continue until a preferred verdict appears. Every per-lock timing verdict +is visible; aggregate time never hides a slower lock. A confirmed regression or +quality regression returns a nonzero CLI status. Inconclusive evidence is retained +for explicit release review; release readiness requires acceptance of that exact +evidence, not merely passing unit tests. + +## Memory and environment limits + +Memory is measured separately in fresh Node child processes, never inside the +timing process. Each implementation has one isolated observation per lock. These +individual observations are explicitly inconclusive about regressions because one +sample cannot estimate environmental variation. A second measurement series runs +the entire catalog in fresh processes, rotating candidate, base and unchanged +controls for five repetitions by default; a non-pass gets one bounded repeat. +The whole-catalog comparison concerns peak RSS of that workload, not the sum of +per-lock RSS. A smoke run restricts both timing and memory to the same two selected fixtures. + +On Linux the metric is `/proc/self/status` **VmHWM × 1024 bytes**, the current +process image's RSS high-water. The raw Node `process.resourceUsage().maxRSS × 1024` +is also retained: controlled investigation showed that it could retain a heavy +launcher's pre-exec high-water, producing an artificial floor in otherwise fresh +workers. Each sample includes startup/post-import/current-image peaks, current RSS, +raw getrusage peak, and a flag when the startup getrusage peak exceeds VmHWM. + +The [Linux kernel documentation](https://docs.kernel.org/filesystems/proc.html) +defines VmHWM and warns that scalable RSS accounting is asynchronous and approximate. +The [Node API documentation](https://nodejs.org/api/process.html#processresourceusage) +specifies maxRSS in kibibytes. Neither metric is exact algorithm allocation: they +include runtime, modules, fixtures and measurement infrastructure. On other +platforms raw Node maxRSS is labeled as a fallback and the memory verdict remains +inconclusive until the platform's process-image semantics are verified. No values +are pooled across platforms. Browser peak memory remains explicitly unavailable; +Node RSS is not browser heap or Web Worker memory. + +Every run records Node/V8/OS/CPU, runtime arguments, candidate/base revisions, +dirty state, TypeScript and harness hashes, exact ESM SHA256, four library +bundle sizes/hashes plus the separate Node CLI bundle, fixture/source pins, compared snapshot SHA256, samples, +paths, metrics and per-lock differences. See the algorithm documentation for +worst-case time and space complexity: these 45 measured configurations are not a +random sample and cannot establish asymptotic complexity or universal speedups. + +An isolated memory correction can retain earlier timing samples without rewriting +them: + +```bash +node scripts/benchmark-memory.ts /path/to/prior/benchmark.json \ + --reference /path/to/unlockmyloot --output-dir docs/benchmarks +``` + +The correction checks that candidate bytes are unchanged, creates a separate RSS +supplement and adds an explicit superseded-memory notice next to the original +report. The original hashed JSON/Markdown/evidence files remain unchanged. + +## Reports and deliberate snapshot updates + +Each successful correctness run atomically publishes a new `run-*` directory: + +- `benchmark.json`: complete paths, raw samples, calibration, differences, + memory observations and provenance. +- `benchmark.md`: English review report, totals and per-lock tables. +- `benchmark-evidence.json`: versioned release evidence with report SHA256 links, + source/artifact provenance, verdicts and the candidate `qualitySnapshot`. + +Existing reports and snapshots are never overwritten automatically. To establish +quality from the actual PR target code, or propose a separate snapshot update: + +```bash +node scripts/benchmark-snapshot.ts \ + --source-module /path/to/base/src/index.mjs \ + --source-format legacy \ + --reason 'Initial migration baseline from the actual PR target revision' \ + --output /new/path/base-quality.json +``` + +The explicit generator verifies all 45 paths, their exact minima and determinism, +records the source revision/hash and reason, and creates the destination +exclusively. An existing destination causes an error. It does not establish a +performance pass. Review any proposed new snapshot together with a matched +comparison; do not regenerate expected quality from candidate code inside PR CI. +For a previous release, use its `qualitySnapshot` from verified evidence and the +exact previously published ESM bytes as the baseline. + +`pnpm benchmark:vitest` runs representative fixed inputs through Vitest 5's test +context `bench.compare()` for day-to-day investigation. This developer report is +not the release gate and writes no tracked snapshot. The controlled harness above +is authoritative for matched environment evidence. The current API is documented +in the [official Vitest benchmarking guide](https://vitest.dev/guide/benchmarking). diff --git a/benchmarks/catalog.mjs b/benchmarks/catalog.mjs deleted file mode 100644 index ca64991..0000000 --- a/benchmarks/catalog.mjs +++ /dev/null @@ -1,26 +0,0 @@ -import { createHash } from 'node:crypto'; -import { readFileSync } from 'node:fs'; - -export const catalogUrl = new URL('./fixtures/catalog.json', import.meta.url); - -export function sha256(value) { - return createHash('sha256').update(value).digest('hex'); -} - -export function loadManifest() { - return JSON.parse(readFileSync(new URL('./fixtures/manifest.json', import.meta.url), 'utf8')); -} - -export function loadCatalog() { - const bytes = readFileSync(catalogUrl); - const manifest = loadManifest(); - if (sha256(bytes) !== manifest.catalogSha256) { - throw new Error('Catalog data hash differs from its pinned manifest.'); - } - const catalog = JSON.parse(bytes.toString('utf8')); - if (!Array.isArray(catalog) || catalog.length !== manifest.count - || new Set(catalog.map((lock) => lock.id)).size !== manifest.count) { - throw new Error('Catalog must contain the 45 distinct pinned inputs.'); - } - return catalog; -} diff --git a/benchmarks/catalog.ts b/benchmarks/catalog.ts new file mode 100644 index 0000000..4b61a1d --- /dev/null +++ b/benchmarks/catalog.ts @@ -0,0 +1,61 @@ +import { createHash } from 'node:crypto'; +import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { array, integer, legacyCommands, record, string, type Definition, type Metrics, type TupleCommand } from './validation.ts'; + +export const catalogSha256 = '0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675'; +export const referenceRevision = 'eee0bf50ebcb7fffd2b47954fd76bb015854e365'; +export type Fixture = { id: string; title: string; definition: Definition; expectedActions: number; + baselineCommands: readonly TupleCommand[]; historicalReference: Metrics & { commands: readonly TupleCommand[] } }; +export type SourcePin = { revision: string; solver: { path: string; sha256: string; blockSha256: string; startMarker: string; endMarker: string } }; +export function sha256(value: string | Uint8Array): string { return createHash('sha256').update(value).digest('hex'); } +export function jsonFile(path: string | URL): unknown { const parsed: unknown = JSON.parse(readFileSync(path, 'utf8')); return parsed; } +export function loadManifest(): Record { return record(jsonFile(new URL('./fixtures/manifest.json', import.meta.url))); } +export function readMetrics(value: unknown): Metrics { + const item = record(value, 'metrics'); + return { A: integer(item['A']), U: integer(item['U']), C: integer(item['C']), plateSwitches: integer(item['plateSwitches']) }; +} +export function loadCatalog(): Fixture[] { + const bytes = readFileSync(new URL('./fixtures/catalog.json', import.meta.url)); + if (sha256(bytes) !== catalogSha256 || loadManifest()['catalogSha256'] !== catalogSha256) throw new Error('Catalog hash differs from the immutable 45-fixture pin.'); + const parsed: unknown = JSON.parse(bytes.toString('utf8')); + const locks = array(parsed, 'catalog').map((raw): Fixture => { + const lock = record(raw, 'fixture'); + const definition = record(lock['definition']); + const historical = record(lock['historicalReference']); + return { id: string(lock['id']), title: string(lock['titleEn'] ?? lock['title']), expectedActions: integer(lock['expectedActions']), + definition: { state: array(definition['state']).map((value) => integer(value)), links: array(definition['links']).map((row) => array(row).map((value) => integer(value))) }, + baselineCommands: legacyCommands(lock['baselineCommands']), historicalReference: { ...readMetrics(historical), commands: legacyCommands(historical['commands']) } }; + }); + if (locks.length !== 45 || new Set(locks.map((lock) => lock.id)).size !== 45) throw new Error('Expected 45 unique catalog locks.'); + return locks; +} +export function git(directory: string, args: readonly string[]): string { + return execFileSync('git', ['-C', directory, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); +} +export function verifySource(directory: string | URL): Record { + const root = typeof directory === 'string' ? directory : fileURLToPath(directory); + const manifest = record(jsonFile(join(root, 'manifest.json')), 'source manifest'); + for (const [path, expected] of Object.entries(record(manifest['files']))) { + if (path.startsWith('/') || path.split('/').includes('..')) throw new Error('Unsafe source manifest path.'); + if (sha256(readFileSync(join(root, path))) !== string(expected)) throw new Error(`Source hash mismatch: ${path}`); + } + return manifest; +} +export function upstreamPin(): SourcePin { + const source = record(loadManifest()['source']); const solver = record(source['solver']); + if (source['revision'] !== referenceRevision) throw new Error('Unexpected upstream reference revision pin.'); + return { revision: referenceRevision, solver: { path: string(solver['path']), sha256: string(solver['sha256']), blockSha256: string(solver['blockSha256']), + startMarker: string(solver['startMarker']), endMarker: string(solver['endMarker']) } }; +} +export function verifyReferenceSource(input: { revision: string; html: string }, pin: SourcePin = upstreamPin()): string { + if (input.revision !== pin.revision) throw new Error(`Reference revision mismatch: ${input.revision}`); + if (sha256(input.html) !== pin.solver.sha256) throw new Error('Reference file hash mismatch.'); + const start = input.html.indexOf(pin.solver.startMarker); const end = input.html.indexOf(pin.solver.endMarker, start); + if (start < 0 || end < start) throw new Error('Reference block markers are missing.'); + const block = input.html.slice(start, end + pin.solver.endMarker.length); + if (sha256(block) !== pin.solver.blockSha256) throw new Error('Reference block hash mismatch.'); + return block; +} diff --git a/benchmarks/cli.ts b/benchmarks/cli.ts new file mode 100644 index 0000000..4f7dbe8 --- /dev/null +++ b/benchmarks/cli.ts @@ -0,0 +1,107 @@ +import { readFileSync, readdirSync, statSync } from 'node:fs'; +import os from 'node:os'; +import { dirname, join, relative, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { catalogSha256, git, loadCatalog, sha256, upstreamPin, verifySource } from './catalog.ts'; +import { loadImplementation, loadUpstream, publishReport, readSnapshot, runComparison, type Implementation, type ModuleFormat } from './harness.ts'; +import { collectMemory, type MemorySpec } from './memory.ts'; +import { combineVerdicts } from './comparison.ts'; +const root = fileURLToPath(new URL('../', import.meta.url)); +export type BenchmarkOptions = { candidateModule: string; baselineModule: string; baselineSnapshot: string; + baselineFormat: ModuleFormat; reference?: string; outputDir: string; warmups: number; repetitions: number; + memoryRepetitions: number; smoke: boolean; skipMemory: boolean; help: boolean }; +export function parseBenchmarkArgs(args: readonly string[], compare = false): BenchmarkOptions { + const options: BenchmarkOptions = { candidateModule: join(root, 'dist/gothic-lock-solver.mjs'), + baselineModule: join(root, 'benchmarks/reference-matrix/src/index.mjs'), + baselineSnapshot: join(root, 'benchmarks/snapshots/reference-matrix.json'), baselineFormat: compare ? 'tuple' : 'legacy', + outputDir: join(root, 'docs/benchmarks'), warmups: 2, repetitions: 7, memoryRepetitions: 5, smoke: false, skipMemory: false, help: false }; + const seen = new Set(); + for (let index = 0; index < args.length; index += 1) { + const argument = args[index]; if (argument === undefined) throw new Error('Missing argument.'); + if (seen.has(argument)) throw new Error(`Duplicate argument: ${argument}`); seen.add(argument); + if (argument === '--smoke') { options.smoke = true; continue; } + if (argument === '--skip-memory') { options.skipMemory = true; continue; } + if (argument === '--help') { options.help = true; continue; } + const value = args[++index]; if (value === undefined || value === '' || value.startsWith('--')) throw new Error(`Missing argument value: ${argument}`); + switch (argument) { + case '--candidate-module': options.candidateModule = resolve(value); break; + case '--baseline-module': options.baselineModule = resolve(value); break; + case '--baseline-snapshot': options.baselineSnapshot = resolve(value); break; + case '--baseline-format': if (value !== 'tuple' && value !== 'legacy') throw new Error('Baseline format must be tuple or legacy.'); options.baselineFormat = value; break; + case '--reference': options.reference = resolve(value); break; + case '--output-dir': options.outputDir = resolve(value); break; + case '--warmups': case '--repetitions': case '--memory-repetitions': { + if (!/^(0|[1-9][0-9]*)$/.test(value)) throw new Error('Expected a nonnegative integer.'); + const count = Number(value); const minimum = argument === '--warmups' ? 0 : 1; + if (!Number.isSafeInteger(count) || count < minimum) throw new Error('Invalid repetition/warmup integer.'); + if (argument === '--warmups') options.warmups = count; + else if (argument === '--repetitions') options.repetitions = count; + else options.memoryRepetitions = count; + break; + } + default: throw new Error(`Unknown argument: ${argument}`); + } + } + if (compare && !options.help && (!seen.has('--baseline-module') || !seen.has('--baseline-snapshot'))) throw new Error('Comparison requires explicit --baseline-module and --baseline-snapshot from the target branch or previous release.'); + return options; +} +function hashFiles(directory: string, matches: (path: string) => boolean): Record { + const result: Record = {}; + const visit = (path: string): void => { + for (const entry of readdirSync(path, { withFileTypes: true })) { + const full = join(path, entry.name); + if (entry.isDirectory()) visit(full); + else if (entry.isFile() && matches(full)) result[relative(directory, full)] = sha256(readFileSync(full)); + } + }; + visit(directory); return Object.fromEntries(Object.entries(result).sort(([a], [b]) => a.localeCompare(b))); +} +function revision(directory: string): string | null { try { return git(directory, ['rev-parse', 'HEAD']); } catch { return null; } } +export function collectMetadata(options: BenchmarkOptions) { + const baselineSnapshot = readSnapshot(options.baselineSnapshot); + const moduleHash = sha256(readFileSync(options.baselineModule)); + if (baselineSnapshot.source['moduleSha256'] !== moduleHash) throw new Error('Baseline module hash differs from supplied snapshot source.'); + const runtimeFiles = ['gothic-lock-solver.mjs', 'gothic-lock-solver.min.mjs', 'gothic-lock-solver.js', 'gothic-lock-solver.min.js', 'gothic-lock-solver.cli.mjs']; + const artifacts = Object.fromEntries(runtimeFiles.map((name) => { + const path = join(root, 'dist', name); return [name, { bytes: statSync(path).size, sha256: sha256(readFileSync(path)) }]; + })); + return { environment: { node: process.version, v8: process.versions.v8, platform: process.platform, arch: process.arch, + cpu: os.cpus()[0]?.model ?? 'unknown', logicalCpus: os.cpus().length, osRelease: os.release(), totalMemoryBytes: os.totalmem(), execArgv: process.execArgv }, + source: { revision: revision(root), dirty: git(root, ['status', '--porcelain', '--untracked-files=normal']).length > 0, + files: hashFiles(join(root, 'src'), (path) => path.endsWith('.ts')), harness: hashFiles(join(root, 'benchmarks'), (path) => path.endsWith('.ts')), + scripts: hashFiles(join(root, 'scripts'), (path) => /benchmark[^/]*\.ts$/.test(path)) }, + candidate: { revision: revision(dirname(options.candidateModule)), moduleSha256: sha256(readFileSync(options.candidateModule)), format: 'tuple', module: options.candidateModule }, + baseline: { ...baselineSnapshot.source, module: options.baselineModule, moduleTree: hashFiles(dirname(options.baselineModule), (path) => path.endsWith('.mjs') || path.endsWith('.ts')), snapshotSha256: sha256(readFileSync(options.baselineSnapshot)), snapshot: options.baselineSnapshot, + kind: options.baselineModule.includes('reference-matrix') ? 'reference-baseline' : 'target-branch-or-previous-release' }, + fixtures: { count: 45, catalogSha256, manifestSha256: sha256(readFileSync(new URL('./fixtures/manifest.json', import.meta.url))) }, + originalBfs: verifySource(new URL('./baseline/', import.meta.url)), matrixReference: verifySource(new URL('./reference-matrix/', import.meta.url)), + upstream: { ...upstreamPin(), measured: options.reference !== undefined, externalCheckout: options.reference ?? null, + license: 'External AGPL source is hash-verified and executed only from the supplied pinned checkout; no upstream solver code is vendored.' }, artifacts }; +} +export async function main(compare: boolean): Promise { + const options = parseBenchmarkArgs(process.argv.slice(2), compare); + if (options.help) { + console.log('Usage: pnpm benchmark [--reference PATH] [--output-dir PATH] [--warmups 2] [--repetitions 7] [--memory-repetitions 5] [--smoke] [--skip-memory]'); + console.log('Compare additionally requires --baseline-module PATH --baseline-snapshot PATH [--baseline-format tuple|legacy]. Candidate defaults to the built ESM; --candidate-module PATH overrides it.'); + return; + } + const metadata = collectMetadata(options); + const candidate = await loadImplementation(options.candidateModule, 'tuple', 'Candidate TypeScript library'); + const baseline = await loadImplementation(options.baselineModule, options.baselineFormat, 'Target/reference baseline'); + const bfsModule = join(root, 'benchmarks/baseline/src/index.mjs'); + const bfs = await loadImplementation(bfsModule, 'legacy', 'Original Apache BFS'); bfs.expectedPath = 'bfs'; + const others: Record = { originalBfs: bfs }; + const memorySpecs: MemorySpec[] = [ { name: 'candidate', module: options.candidateModule, format: 'tuple' }, + { name: 'baseline', module: options.baselineModule, format: options.baselineFormat }, { name: 'originalBfs', module: bfsModule, format: 'legacy' } ]; + if (options.reference !== undefined) { others['upstream'] = loadUpstream(options.reference); memorySpecs.push({ name: 'upstream', module: options.reference, format: 'upstream' }); } + const catalog = loadCatalog(); const locks = options.smoke ? catalog.slice(0, 2) : catalog; + const report = runComparison({ locks, candidate, baseline, others, baselineSnapshot: readSnapshot(options.baselineSnapshot), repetitions: options.repetitions, warmups: options.warmups, + onProgress: (completed, total, id) => process.stderr.write(`[${completed}/${total}] ${id}: replay and optimum checked\n`) }); + const memory = options.skipMemory ? { performance: { verdict: 'inconclusive' as const }, available: false, reason: 'Memory explicitly skipped; no memory pass is established.', browser: { available: false } } + : collectMemory(locks, memorySpecs, options.memoryRepetitions, (line) => process.stderr.write(`${line}\n`)); + report.performance = combineVerdicts([report.performance, memory.performance.verdict]); + const paths = await publishReport(report, options.outputDir, metadata, memory); + console.log(JSON.stringify({ ...paths, lockCount: locks.length, quality: report.quality, performance: report.performance }, null, 2)); + // An uncertain result is retained as evidence for explicit review; deterministic failures and confirmed performance regressions fail CI. + if (report.quality === 'regression' || report.performance === 'regression') process.exitCode = 1; +} diff --git a/benchmarks/comparison.ts b/benchmarks/comparison.ts new file mode 100644 index 0000000..352c8e6 --- /dev/null +++ b/benchmarks/comparison.ts @@ -0,0 +1,53 @@ +import type { Metrics } from './validation.ts'; +export type Verdict = 'passed' | 'regression' | 'inconclusive'; +export const metricNames = ['A', 'U', 'C', 'plateSwitches'] as const; + +export function compareMetrics(candidate: Metrics, baseline: Metrics): { quality: 'passed' | 'regression'; deltas: Metrics; regressions: string[] } { + const deltas = { A: candidate.A - baseline.A, U: candidate.U - baseline.U, + C: candidate.C - baseline.C, plateSwitches: candidate.plateSwitches - baseline.plateSwitches }; + const regressions = metricNames.filter((name) => deltas[name] > 0); + return { quality: regressions.length === 0 ? 'passed' : 'regression', deltas, regressions }; +} +export function statistics(samples: readonly number[]): { min: number; median: number; p95: number; max: number } { + if (samples.length === 0 || samples.some((sample) => !Number.isFinite(sample) || sample < 0)) throw new Error('Invalid timing samples.'); + const sorted = [...samples].sort((a, b) => a - b); + const first = sorted[0]; const last = sorted.at(-1); + const middle = Math.floor(sorted.length / 2); + const high = sorted[middle]; const low = sorted[Math.max(0, middle - 1)]; + const p95 = sorted[Math.ceil(sorted.length * 0.95) - 1]; + if (first === undefined || last === undefined || high === undefined || low === undefined || p95 === undefined) throw new Error('Missing sample.'); + return { min: first, median: sorted.length % 2 === 0 ? (low + high) / 2 : high, p95, max: last }; +} +function interval(samples: readonly number[]): { mean: number; lower: number; upper: number } { + const mean = samples.reduce((sum, sample) => sum + sample, 0) / samples.length; + const variance = samples.reduce((sum, sample) => sum + (sample - mean) ** 2, 0) / Math.max(1, samples.length - 1); + // Conservative t bound >= two-sided 95% t critical for n >= 5. No claimed exact CI. + const radius = 2.776 * Math.sqrt(variance / samples.length); + return { mean, lower: mean - radius, upper: mean + radius }; +} +export function performanceVerdict(candidate: readonly number[], baseline: readonly number[], selfA: readonly number[], selfB: readonly number[]) { + const missing = { verdict: 'inconclusive' as Verdict, reason: 'At least five matched positive samples and unchanged-vs-itself calibration are required.', + ratio: null, noiseRatio: null, lowerRatio: null, upperRatio: null }; + if (candidate.length < 5 || candidate.length !== baseline.length || selfA.length < 5 || selfA.length !== selfB.length + || [...candidate, ...baseline, ...selfA, ...selfB].some((sample) => !Number.isFinite(sample) || sample <= 0)) return missing; + const logs = (a: readonly number[], b: readonly number[]): number[] => a.map((value, index) => { + const other = b[index]; if (other === undefined) throw new Error('Unpaired sample.'); return Math.log(value / other); + }); + const self = interval(logs(selfA, selfB)); + const measured = interval(logs(candidate, baseline)); + const noise = Math.max(Math.abs(self.lower), Math.abs(self.upper)); + // A noisy control cannot certify equivalence. A clear slowdown still remains a regression. + const verdict: Verdict = measured.lower > noise ? 'regression' + : noise <= Math.log(1.25) && measured.upper <= noise ? 'passed' : 'inconclusive'; + return { verdict, reason: verdict === 'regression' ? 'Matched slowdown exceeds the unchanged-control uncertainty envelope.' + : verdict === 'passed' ? 'Measured upper slowdown bound lies within the calibrated control envelope.' + : 'Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.', + ratio: Math.exp(measured.mean), noiseRatio: Math.exp(noise), lowerRatio: Math.exp(measured.lower), upperRatio: Math.exp(measured.upper) }; +} +export function combineVerdicts(values: readonly Verdict[]): Verdict { + return values.includes('regression') ? 'regression' : values.includes('inconclusive') ? 'inconclusive' : 'passed'; +} +export function confirmPerformance(first: Verdict, second: Verdict): Verdict { + // A bounded repeat never erases the first signal. Conflicting observations are uncertainty. + return first === second ? first : 'inconclusive'; +} diff --git a/benchmarks/harness.mjs b/benchmarks/harness.mjs deleted file mode 100644 index e7e8577..0000000 --- a/benchmarks/harness.mjs +++ /dev/null @@ -1,403 +0,0 @@ -import assert from 'node:assert/strict'; -import { execFileSync } from 'node:child_process'; -import { randomUUID } from 'node:crypto'; -import { readFileSync, readdirSync } from 'node:fs'; -import { mkdir, mkdtemp, rename, rm, writeFile } from 'node:fs/promises'; -import os from 'node:os'; -import { join, relative, resolve } from 'node:path'; -import { performance } from 'node:perf_hooks'; -import { fileURLToPath } from 'node:url'; -import { solveLock } from '../src/index.mjs'; -import { solveLock as baselineSolveLock } from './baseline/src/index.mjs'; -import { loadCatalog, loadManifest, sha256 } from './catalog.mjs'; -import { commandMetrics, groupReferenceClicks, replayCommands } from './validation.mjs'; - -const root = fileURLToPath(new URL('../', import.meta.url)); -const baselineRoot = fileURLToPath(new URL('./baseline/', import.meta.url)); - -export function statistics(samples) { - if (!Array.isArray(samples) || samples.length === 0 - || samples.some((sample) => !Number.isFinite(sample) || sample < 0)) { - throw new Error('Timing samples must be a nonempty array of finite nonnegative numbers.'); - } - const sorted = [...samples].sort((a, b) => a - b); - const middle = Math.floor(sorted.length / 2); - return { - min: sorted[0], - median: sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2, - max: sorted.at(-1), - }; -} - -function validateCount(value, name, minimum) { - if (!Number.isSafeInteger(value) || value < minimum) { - throw new Error(`${name} must be a safe integer >= ${minimum}.`); - } -} - -export function parseBenchmarkArgs(args) { - const options = { repetitions: 5, warmups: 1, outputDir: 'docs/benchmarks', reference: null, smoke: false, help: false }; - const seen = new Set(); - const fields = { '--repetitions': 'repetitions', '--warmups': 'warmups', - '--output-dir': 'outputDir', '--reference': 'reference' }; - for (let index = 0; index < args.length; index += 1) { - const argument = args[index]; - if (seen.has(argument)) throw new Error(`Duplicate argument: ${argument}`); - seen.add(argument); - if (argument === '--smoke' || argument === '--help') { - options[argument.slice(2)] = true; - continue; - } - const field = fields[argument]; - if (!field) throw new Error(`Unknown argument: ${argument}`); - const value = args[++index]; - if (value === undefined || value.length === 0 || value.startsWith('--')) { - throw new Error(`Missing argument value: ${argument}`); - } - if (field === 'repetitions' || field === 'warmups') { - if (!/^(0|[1-9][0-9]*)$/.test(value)) throw new Error(`${argument} requires an integer.`); - options[field] = Number(value); - } else options[field] = value; - } - validateCount(options.repetitions, 'repetitions', 1); - validateCount(options.warmups, 'warmups', 0); - return options; -} - -function git(directory, args) { - return execFileSync('git', ['-C', directory, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); -} - -export function verifyBaseline() { - const manifest = JSON.parse(readFileSync(join(baselineRoot, 'manifest.json'), 'utf8')); - for (const [path, expected] of Object.entries(manifest.files)) { - if (sha256(readFileSync(join(baselineRoot, path))) !== expected) { - throw new Error(`Apache baseline source hash mismatch: ${path}`); - } - } - return manifest; -} - -export function verifyReferenceSource({ revision, html }, source = loadManifest().source) { - if (revision !== source.revision) { - throw new Error(`Reference revision mismatch: expected ${source.revision}, received ${revision}.`); - } - if (sha256(html) !== source.solver.sha256) throw new Error('Reference index.html source hash mismatch.'); - const text = Buffer.isBuffer(html) ? html.toString('utf8') : html; - const start = text.indexOf(source.solver.startMarker); - const end = text.indexOf(source.solver.endMarker, start); - if (start < 0 || end < start) throw new Error('Reference solver markers are missing.'); - const block = text.slice(start, end + source.solver.endMarker.length); - if (sha256(block) !== source.solver.blockSha256) throw new Error('Reference solver block hash mismatch.'); - return block; -} - -export function loadReference(directory) { - const source = loadManifest().source; - const checkout = resolve(directory); - let revision; - try { revision = git(checkout, ['rev-parse', 'HEAD']); } catch { - throw new Error(`Cannot read reference checkout revision: ${checkout}`); - } - const html = readFileSync(join(checkout, source.solver.path)); - const block = verifyReferenceSource({ revision, html }, source); - // The hash-verified, unchanged upstream block is compiled in this Node realm. - // This avoids cross-realm Array/typed-array overhead and does not vendor AGPL code. - const reference = Function(`${block}\n; return solveLock;`)(); - if (typeof reference !== 'function') throw new Error('Reference solveLock function is missing.'); - return { - implementation: { - label: 'UnlockMyLoot (live pinned reference)', requiresOptimality: false, - solve: (definition) => reference(definition.state, definition.links), - normalize: (sequence) => { - const commands = groupReferenceClicks(sequence); - return { status: commands === null ? 'unsolvable' : 'solved', commands }; - }, - }, - metadata: { measured: true, repository: source.repository, revision, path: source.solver.path, - fileSha256: source.solver.sha256, solverBlockSha256: source.solver.blockSha256, - execution: 'Unchanged source compiled with Function in the same Node.js realm; no browser or vm context.' }, - }; -} - -export function defaultImplementations(reference = null) { - const normalize = (result) => result; - const implementations = { - 'matrix-astar': { label: 'Matrix A*', solve: (definition) => solveLock(definition, { algorithm: 'matrix-astar' }), normalize, requiresOptimality: true }, - bfs: { label: 'Current exact BFS', solve: (definition) => solveLock(definition, { algorithm: 'bfs' }), normalize, requiresOptimality: true }, - baseline: { label: 'Original Apache BFS', solve: baselineSolveLock, normalize, requiresOptimality: true }, - }; - if (reference) implementations.reference = reference; - return implementations; -} - -function hashTree(directory, predicate = () => true) { - const files = {}; - function visit(path) { - for (const entry of readdirSync(path, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) { - const full = join(path, entry.name); - if (entry.isDirectory()) visit(full); - else if (entry.isFile() && predicate(full)) files[relative(directory, full)] = sha256(readFileSync(full)); - } - } - visit(directory); - return { sha256: sha256(JSON.stringify(files)), files }; -} - -export function collectMetadata(referenceMetadata = null) { - let revision = null; let dirty = null; - try { - revision = git(root, ['rev-parse', 'HEAD']); - dirty = git(root, ['status', '--porcelain', '--untracked-files=normal']).length > 0; - } catch { /* Source hashes remain available outside a Git checkout. */ } - const manifest = loadManifest(); - return { - environment: { node: process.version, v8: process.versions.v8, platform: process.platform, - arch: process.arch, osRelease: os.release(), cpu: os.cpus()[0]?.model ?? null, - logicalCpus: os.cpus().length, totalMemoryBytes: os.totalmem(), execArgv: process.execArgv }, - source: { repository: 'https://github.com/Marcus-Rise/gothic-lock-solver', revision, - dirty, core: hashTree(join(root, 'src'), (path) => path.endsWith('.mjs')), - harness: { ...hashTree(join(root, 'benchmarks'), (path) => path.endsWith('.mjs')), - cliSha256: sha256(readFileSync(join(root, 'scripts/benchmark.mjs'))) } }, - fixtures: { count: manifest.count, catalogSha256: manifest.catalogSha256, - manifestSha256: sha256(readFileSync(new URL('./fixtures/manifest.json', import.meta.url))), - repository: manifest.source.repository, revision: manifest.source.revision }, - baseline: verifyBaseline(), - reference: referenceMetadata ?? { measured: false, reason: 'No --reference checkout supplied; cached metrics are not live timings.', - repository: manifest.source.repository, revision: manifest.source.revision }, - }; -} - -function assertMetricsEqual(actual, expected, message) { - for (const field of ['A', 'U', 'C', 'plateSwitches']) { - assert.equal(actual[field], expected[field], `${message}: ${field}`); - } -} - -function validateResult(lock, name, implementation, result) { - assert.equal(result?.status, 'solved', `${lock.id}/${name}: expected a solved result.`); - const finalState = replayCommands(lock.definition, result.commands); - assert.deepEqual(finalState, lock.definition.state.map(() => 4), `${lock.id}/${name}: target state not reached.`); - const metrics = commandMetrics(result.commands); - if (implementation.requiresOptimality) { - assert.equal(metrics.A, lock.expectedActions, `${lock.id}/${name}: wrong minimum action count.`); - } else { - assert.ok(metrics.A >= lock.expectedActions, `${lock.id}/${name}: reference beats the claimed exact minimum.`); - } - if (name === 'bfs' || name === 'baseline') { - assert.deepEqual(result.commands, lock.baselineCommands, `${lock.id}/${name}: original BFS order changed.`); - } - if (name === 'reference') { - assertMetricsEqual(metrics, lock.historicalReference, `${lock.id}: historical reference metric drift`); - assert.deepEqual(result.commands, lock.historicalReference.commands, `${lock.id}: reference path drift.`); - } - if (result.metrics) { - assert.equal(result.metrics.commands, metrics.A, `${lock.id}/${name}: result action metric mismatch.`); - assert.equal(result.metrics.divisions, metrics.C, `${lock.id}/${name}: result division metric mismatch.`); - assert.equal(result.metrics.plateSwitches, metrics.plateSwitches, `${lock.id}/${name}: result switch metric mismatch.`); - } - for (const [field, expected] of [['initialState', lock.definition.state], ['targetState', finalState], ['finalState', finalState]]) { - if (result[field]) assert.deepEqual(result[field], expected, `${lock.id}/${name}: invalid ${field}.`); - } - return { ...metrics, commands: structuredClone(result.commands) }; -} - -function metricComparison(rows, getComparator, field) { - const result = { wins: 0, ties: 0, losses: 0 }; - for (const row of rows) { - const matrix = row.solvers['matrix-astar'][field]; - const other = getComparator(row)[field]; - result[matrix < other ? 'wins' : matrix > other ? 'losses' : 'ties'] += 1; - } - return result; -} - -function summarize(rows, names) { - const aggregates = {}; - for (const name of names) { - aggregates[name] = rows.reduce((total, row) => { - const result = row.solvers[name]; - for (const field of ['A', 'U', 'C']) total[field] += result[field]; - total.sumOfMediansMs += result.timingMs.median; - return total; - }, { A: 0, U: 0, C: 0, sumOfMediansMs: 0 }); - } - const comparisons = {}; - for (const name of [...names.filter((name) => name !== 'matrix-astar'), 'historicalReference']) { - const historic = name === 'historicalReference'; - const get = (row) => historic ? row.historicalReference : row.solvers[name]; - comparisons[name] = { actions: metricComparison(rows, get, 'A'), - uniquePlates: metricComparison(rows, get, 'U'), clicks: metricComparison(rows, get, 'C'), - timingCompared: !historic }; - if (!historic) { - const values = rows.map((row) => ({ A: row.solvers['matrix-astar'].timingMs.median, B: get(row).timingMs.median })); - const ratios = values.map(({ A, B }) => A > 0 && B > 0 ? B / A : null); - comparisons[name].timing = { - wins: values.filter(({ A, B }) => A < B).length, - ties: values.filter(({ A, B }) => A === B).length, - losses: values.filter(({ A, B }) => A > B).length, - geometricMeanSpeedup: ratios.every((value) => value !== null) - ? Math.exp(ratios.reduce((sum, value) => sum + Math.log(value), 0) / ratios.length) : null, - ratioOfSummedMedians: aggregates['matrix-astar'].sumOfMediansMs > 0 - ? aggregates[name].sumOfMediansMs / aggregates['matrix-astar'].sumOfMediansMs : null, - }; - } - } - return { aggregates, comparisons }; -} - -export function runBenchmark({ locks = loadCatalog(), implementations = defaultImplementations(), - repetitions = 5, warmups = 1, metadata = {}, clock = () => performance.now(), - onProgress = null } = {}) { - validateCount(repetitions, 'repetitions', 1); - validateCount(warmups, 'warmups', 0); - if (!Array.isArray(locks) || locks.length === 0) throw new Error('At least one benchmark lock is required.'); - if (new Set(locks.map((lock) => lock.id)).size !== locks.length) throw new Error('Duplicate benchmark lock identifiers.'); - const names = Object.keys(implementations); - for (const name of ['matrix-astar', 'bfs', 'baseline']) { - if (!implementations[name]) throw new Error(`Missing benchmark implementation: ${name}`); - } - const results = []; - for (const [lockIndex, lock] of locks.entries()) { - validateCount(lock.expectedActions, 'expectedActions', 0); - const historicalMetrics = commandMetrics(lock.historicalReference.commands); - assertMetricsEqual(historicalMetrics, lock.historicalReference, `${lock.id}: cached historical reference metrics`); - assert.deepEqual(replayCommands(lock.definition, lock.historicalReference.commands), - lock.definition.state.map(() => 4), `${lock.id}: cached reference path must reach target.`); - const baselineMetrics = commandMetrics(lock.baselineCommands); - assert.equal(baselineMetrics.A, lock.expectedActions, `${lock.id}: cached BFS minimum mismatch.`); - assert.deepEqual(replayCommands(lock.definition, lock.baselineCommands), - lock.definition.state.map(() => 4), `${lock.id}: cached baseline path must reach target.`); - const measured = Object.fromEntries(names.map((name) => [name, { samplesMs: [], solution: null }])); - for (let round = -warmups; round < repetitions; round += 1) { - const offset = (lockIndex + round + warmups) % names.length; - for (let index = 0; index < names.length; index += 1) { - const name = names[(offset + index) % names.length]; - const implementation = implementations[name]; - const definition = structuredClone(lock.definition); - const before = JSON.stringify(definition); - const start = clock(); - const raw = implementation.solve(definition); - const elapsed = clock() - start; - assert.equal(JSON.stringify(definition), before, `${lock.id}/${name}: solver mutated its input.`); - const solution = validateResult(lock, name, implementation, implementation.normalize(raw)); - const entry = measured[name]; - if (entry.solution) assert.deepEqual(solution, entry.solution, `${lock.id}/${name}: nondeterministic solution.`); - entry.solution = solution; - if (round >= 0) entry.samplesMs.push(elapsed); - } - } - const solvers = Object.fromEntries(names.map((name) => [name, { ...measured[name].solution, - timingMs: statistics(measured[name].samplesMs), samplesMs: measured[name].samplesMs }])); - results.push({ id: lock.id, title: lock.title, plateCount: lock.definition.state.length, - url: lock.url ?? null, code: lock.code ?? null, expectedActions: lock.expectedActions, - solvers, historicalReference: structuredClone(lock.historicalReference) }); - if (onProgress) onProgress({ completed: lockIndex + 1, total: locks.length, id: lock.id }); - } - const inputEvidence = (entries) => JSON.stringify(entries.map(({ id, definition, expectedActions }) => ({ id, definition, expectedActions }))); - const inputs = inputEvidence(locks); - const completeCatalog = inputs === inputEvidence(loadCatalog()); - return { - schemaVersion: 1, createdAt: new Date().toISOString(), - config: { repetitions, warmups, lockCount: locks.length, scope: completeCatalog ? 'full-45-catalog' : 'subset', - inputSha256: sha256(inputs), - units: 'milliseconds', executionOrder: 'Per lock, warmup and measured rounds rotate solver order deterministically.', - timingBoundary: 'Only the synchronous solver call is timed. Input cloning, reference click grouping, replay, metrics, and reporting are outside timing. Current and baseline calls include their public facade validation/result construction.', - gc: 'Natural Node.js garbage collection; no forced GC.' }, - metadata, implementations: Object.fromEntries(names.map((name) => [name, { label: implementations[name].label, - requiresOptimality: implementations[name].requiresOptimality }])), - correctness: { passed: true, lockCount: locks.length, checks: [ - 'Every warmup and measured solution independently replayed one division at a time.', - 'Matrix A*, exact BFS, and original Apache BFS equal the stored exact action minimum.', - 'Current BFS preserves the pinned original BFS command sequence.', - 'Inputs are unchanged and every implementation returns a deterministic path.', - names.includes('reference') ? 'Live reference path and A/U/C equal the cached pinned observation.' - : 'Cached reference paths and metrics are validated; reference was not timed.', - ] }, - ...summarize(results, names), results, - limitations: [ - 'These are 45 fixed catalog configurations, not a random sample or a claim about every game state.', - 'Only actions (A) are minimized by the production solver; distinct plates (U) and unit divisions (C) are descriptive metrics.', - 'The reference minimizes unit divisions before plate switches; its objective differs from minimum grouped actions.', - 'Timing reflects this runtime, machine, sample count, JIT and garbage collection; no timing threshold is a correctness gate.', - 'These are Node.js timings, not measurements in an actual browser or Web Worker.', - 'Cached historical timings are provenance only and are excluded from current speed comparisons.', - 'The sum of per-lock medians is an aggregate of medians, not the median runtime of a whole-catalog pass.', - 'Speedup ratios above 1 favor matrix A*; ratios below 1 favor the comparator. Timing ties require equal measured medians.', - ], - }; -} - -const cell = (value) => String(value ?? '—').replaceAll('|', '\\|').replaceAll('\n', ' '); -const number = (value) => value === null || value === undefined ? '—' : value.toFixed(6); -const counts = (value) => `${value.wins}/${value.ties}/${value.losses}`; - -export function renderMarkdown(report) { - const names = Object.keys(report.implementations); - const lines = ['# Matrix search benchmark', '', - `Generated: ${report.createdAt}. Scope: **${report.config.scope}** (${report.config.lockCount} locks).`, '', - `Correctness gates: **${report.correctness.passed ? 'PASS' : 'FAIL'}**. Warmups per solver/lock: ${report.config.warmups}; measured repetitions: ${report.config.repetitions}.`, '', - 'A = actions (grouped commands); U = unique selected plates; C = unit divisions of the selected plate. All timings are milliseconds.', '', - report.config.timingBoundary, '', report.config.executionOrder, '', - `GC: ${report.config.gc}`, '', '## Runtime and source evidence', '', - '```json', JSON.stringify(report.metadata, null, 2), '```', '', - '## Totals across measured locks', '', - '| Solver | A | U | C | Sum of per-lock median ms |', - '| --- | ---: | ---: | ---: | ---: |']; - for (const name of names) { - const value = report.aggregates[name]; - lines.push(`| ${cell(report.implementations[name].label)} | ${value.A} | ${value.U} | ${value.C} | ${number(value.sumOfMediansMs)} |`); - } - lines.push('', '## Matrix A* comparisons', '', - 'Wins/ties/losses count locks where matrix A* is lower/equal/higher. A ratio above 1 favors matrix A*.', '', - '| Comparator | A wins/ties/losses | U wins/ties/losses | C wins/ties/losses | Time wins/ties/losses | Geometric mean speedup | Ratio of summed medians |', - '| --- | --- | --- | --- | --- | ---: | ---: |'); - for (const [name, value] of Object.entries(report.comparisons)) { - lines.push(`| ${cell(report.implementations[name]?.label ?? 'Reference (cached metrics only)')} | ${counts(value.actions)} | ${counts(value.uniquePlates)} | ${counts(value.clicks)} | ${value.timingCompared ? counts(value.timing) : 'not timed'} | ${number(value.timing?.geometricMeanSpeedup)} | ${number(value.timing?.ratioOfSummedMedians)} |`); - } - lines.push('', '## Per-lock action metrics', '', - `| Lock | N | Exact minimum A | ${names.map((name) => `${cell(report.implementations[name].label)} A/U/C`).join(' | ')} | Cached reference A/U/C |`, - `| --- | ---: | ---: | ${names.map(() => '---').join(' | ')} | --- |`); - for (const row of report.results) { - const metrics = (result) => `${result.A}/${result.U}/${result.C}`; - lines.push(`| ${cell(row.title)} (${row.id}) | ${row.plateCount} | ${row.expectedActions} | ${names.map((name) => metrics(row.solvers[name])).join(' | ')} | ${metrics(row.historicalReference)} |`); - } - lines.push('', '## Per-lock live timings (min / median / max ms)', '', - `| Lock | ${names.map((name) => cell(report.implementations[name].label)).join(' | ')} |`, - `| --- | ${names.map(() => '---').join(' | ')} |`); - for (const row of report.results) { - lines.push(`| ${cell(row.title)} (${row.id}) | ${names.map((name) => { - const timing = row.solvers[name].timingMs; - return `${number(timing.min)} / ${number(timing.median)} / ${number(timing.max)}`; - }).join(' | ')} |`); - } - lines.push('', '## Correctness checks', '', ...report.correctness.checks.map((value) => `- ${value}`), '', - '## Limits of the conclusions', '', ...report.limitations.map((value) => `- ${value}`), '', - 'The JSON report includes individual samples and independently verified command sequences. Cached observations never supply values to the live timing table.', ''); - return lines.join('\n'); -} - -export async function publishReport(report, outputDir) { - if (report?.correctness?.passed !== true) throw new Error('A report cannot be published before correctness gates pass.'); - // Render both artifacts before touching disk; publish the directory with one same-filesystem rename. - const json = `${JSON.stringify(report, null, 2)}\n`; - const markdown = renderMarkdown(report); - const outputRoot = resolve(outputDir); - await mkdir(outputRoot, { recursive: true }); - const staged = await mkdtemp(join(outputRoot, '.benchmark-')); - const directory = join(outputRoot, `run-${report.createdAt.replaceAll(/[^0-9TZ]/g, '')}-${randomUUID().slice(0, 8)}`); - try { - await writeFile(join(staged, 'benchmark.json'), json, { flag: 'wx' }); - await writeFile(join(staged, 'benchmark.md'), markdown, { flag: 'wx' }); - await rename(staged, directory); - } catch (error) { - await rm(staged, { recursive: true, force: true }); - throw error; - } - return { directory, jsonPath: join(directory, 'benchmark.json'), markdownPath: join(directory, 'benchmark.md') }; -} - -export async function runAndPublish({ outputDir, ...options }) { - const report = runBenchmark(options); - return { report, ...await publishReport(report, outputDir) }; -} diff --git a/benchmarks/harness.ts b/benchmarks/harness.ts new file mode 100644 index 0000000..b47ed73 --- /dev/null +++ b/benchmarks/harness.ts @@ -0,0 +1,220 @@ +import assert from 'node:assert/strict'; +import { randomUUID } from 'node:crypto'; +import { mkdir, mkdtemp, rename, rm, writeFile } from 'node:fs/promises'; +import { readFileSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { performance } from 'node:perf_hooks'; +import { pathToFileURL } from 'node:url'; +import { array, commandMetrics, groupReferenceClicks, legacyCommands, record, replayCommands, string, tupleCommands, type Definition, type Metrics, type TupleCommand } from './validation.ts'; +import { catalogSha256, git, jsonFile, loadCatalog, readMetrics, sha256, upstreamPin, verifyReferenceSource, type Fixture } from './catalog.ts'; +import { combineVerdicts, compareMetrics, confirmPerformance, performanceVerdict, statistics } from './comparison.ts'; + +export type ModuleFormat = 'tuple' | 'legacy'; +export type Implementation = { label: string; solve: (definition: Definition) => unknown; + normalize: (raw: unknown) => unknown; requiresOptimality: boolean; expectedPath?: 'bfs' | 'upstream' }; +export type Solution = Metrics & { commands: readonly TupleCommand[] }; +export type Snapshot = { schemaVersion: number; fixtureSha256: string; source: Record; reason: string; + locks: (Solution & { id: string; optimumA: number })[] }; +export type Measurement = Solution & { samplesMs: number[]; timingMs: ReturnType }; + +export async function loadImplementation(modulePath: string, format: ModuleFormat, label: string): Promise { + const module: unknown = await import(pathToFileURL(resolve(modulePath)).href); + const exported = record(module, 'solver module')['solveLock']; + if (typeof exported !== 'function') throw new Error(`Missing named solveLock export: ${modulePath}`); + return { label, requiresOptimality: true, + solve: (definition) => { const raw: unknown = format === 'tuple' ? exported(definition.state, definition.links) : exported(definition); return raw; }, + normalize: (raw) => { + if (format === 'tuple') return raw; + const result = record(raw, 'legacy result'); + if (result['status'] === 'unsolvable') return null; + if (result['status'] !== 'solved') throw new Error('Unexpected legacy solver status.'); + return legacyCommands(result['commands']); + } }; +} +export function loadUpstream(directory: string): Implementation { + const pin = upstreamPin(); + const html = readFileSync(join(directory, pin.solver.path), 'utf8'); + const block = verifyReferenceSource({ revision: git(directory, ['rev-parse', 'HEAD']), html }, pin); + // Executes only the independently hash-verified external source, never copied into this repository. + const exported: unknown = Function(`${block}\n; return solveLock;`)(); + if (typeof exported !== 'function') throw new Error('Pinned upstream solveLock is missing.'); + return { label: 'UnlockMyLoot pinned external reference', requiresOptimality: false, expectedPath: 'upstream', + solve: (definition) => { const raw: unknown = exported(definition.state, definition.links); return raw; }, normalize: groupReferenceClicks }; +} +export function readSnapshot(path: string): Snapshot { + const value = record(jsonFile(path), 'snapshot'); + if (value['schemaVersion'] !== 1 || value['fixtureSha256'] !== catalogSha256) throw new Error('Snapshot schema or fixture hash mismatch.'); + const locks = array(value['locks'], 'snapshot locks').map((raw) => { + const row = record(raw); const metrics = readMetrics(row); const commands = tupleCommands(row['commands']); + assert.deepEqual(commandMetrics(commands), metrics, 'Snapshot metric/path mismatch.'); + if (typeof row['optimumA'] !== 'number' || !Number.isInteger(row['optimumA'])) throw new Error('Snapshot optimum missing.'); + return { id: string(row['id']), optimumA: row['optimumA'], ...metrics, commands }; + }); + if (locks.length !== 45 || new Set(locks.map((lock) => lock.id)).size !== 45) throw new Error('Snapshot requires 45 unique locks.'); + return { schemaVersion: 1, fixtureSha256: catalogSha256, source: record(value['source']), reason: string(value['reason']), locks }; +} +export async function writeSnapshot(path: string, snapshot: unknown): Promise { + // Deliberately exclusive. Refreshes require a new reviewed destination, never an implicit replacement. + await writeFile(path, `${JSON.stringify(snapshot, null, 2)}\n`, { flag: 'wx' }); +} +function validate(lock: Fixture, implementation: Implementation, raw: unknown): Solution { + if (raw === null) throw new Error(`${lock.id}: lost solvability.`); + const commands = tupleCommands(raw); + assert.deepEqual(replayCommands(lock.definition, commands), lock.definition.state.map(() => 4), `${lock.id}: target not reached.`); + const metrics = commandMetrics(commands); + if (implementation.requiresOptimality) assert.equal(metrics.A, lock.expectedActions, `${lock.id}: wrong exact action minimum.`); + else assert.ok(metrics.A >= lock.expectedActions, `${lock.id}: upstream beats claimed optimum.`); + if (implementation.expectedPath === 'bfs') assert.deepEqual(commands, lock.baselineCommands, `${lock.id}: pinned BFS path changed.`); + if (implementation.expectedPath === 'upstream') { + assert.deepEqual(commands, lock.historicalReference.commands, `${lock.id}: pinned upstream path drift.`); + assert.deepEqual(metrics, commandMetrics(lock.historicalReference.commands), `${lock.id}: upstream metrics drift.`); + } + return { ...metrics, commands }; +} +function measure(lock: Fixture, implementation: Implementation, clock: () => number): { solution: Solution; elapsed: number } { + const definition = structuredClone(lock.definition); const before = JSON.stringify(definition); + const start = clock(); const raw = implementation.solve(definition); const elapsed = clock() - start; + assert.equal(JSON.stringify(definition), before, `${lock.id}: solver mutated its input.`); + return { solution: validate(lock, implementation, implementation.normalize(raw)), elapsed }; +} +export function runComparison(options: { + locks?: Fixture[]; candidate: Implementation; baseline: Implementation; baselineSnapshot?: Snapshot; + others?: Record; repetitions?: number; warmups?: number; clock?: () => number; + onProgress?: (completed: number, total: number, id: string) => void; +}) { + const locks = options.locks ?? loadCatalog(); const repetitions = options.repetitions ?? 7; + const warmups = options.warmups ?? 2; const clock = options.clock ?? (() => performance.now()); + if (!Number.isSafeInteger(repetitions) || repetitions < 1 || !Number.isSafeInteger(warmups) || warmups < 0) throw new Error('Invalid repetition/warmup count.'); + if (locks.length === 0 || new Set(locks.map((lock) => lock.id)).size !== locks.length) throw new Error('Expected unique nonempty fixture set.'); + const implementations = { ...options.others, candidate: options.candidate, baseline: options.baseline }; + const entries = Object.entries(implementations); + const results = locks.map((lock, lockIndex) => { + assert.deepEqual(replayCommands(lock.definition, lock.baselineCommands), lock.definition.state.map(() => 4)); + assert.equal(lock.baselineCommands.length, lock.expectedActions, 'Pinned BFS optimum mismatch.'); + assert.deepEqual(replayCommands(lock.definition, lock.historicalReference.commands), lock.definition.state.map(() => 4)); + assert.deepEqual(commandMetrics(lock.historicalReference.commands), readMetrics(lock.historicalReference)); + const selfA: number[] = []; const selfB: number[] = []; + let baselineControl: Solution | undefined; + for (let round = -warmups; round < repetitions; round += 1) { + for (const slot of (round + warmups) % 2 === 0 ? [selfA, selfB] : [selfB, selfA]) { + const measured = measure(lock, options.baseline, clock); + if (baselineControl !== undefined) assert.deepEqual(measured.solution, baselineControl, `${lock.id}: nondeterministic baseline control.`); + baselineControl = measured.solution; + if (round >= 0) slot.push(measured.elapsed); + } + } + const measured = new Map(); + for (const [name] of entries) measured.set(name, { samplesMs: [] }); + for (let round = -warmups; round < repetitions; round += 1) { + const offset = (round + warmups + lockIndex) % entries.length; + for (let index = 0; index < entries.length; index += 1) { + const entry = entries[(offset + index) % entries.length]; if (entry === undefined) throw new Error('Missing implementation.'); + const [name, implementation] = entry; const prior = measured.get(name); if (prior === undefined) throw new Error('Missing measurement.'); + const call = measure(lock, implementation, clock); + if (prior.solution !== undefined) assert.deepEqual(call.solution, prior.solution, `${lock.id}/${name}: nondeterministic path.`); + prior.solution = call.solution; + if (round >= 0) prior.samplesMs.push(call.elapsed); + } + } + const solvers: Record = Object.fromEntries([...measured].map(([name, value]) => { + if (value.solution === undefined) throw new Error('Missing measured solution.'); + return [name, { ...value.solution, samplesMs: value.samplesMs, timingMs: statistics(value.samplesMs) }]; + })); + const candidate = solvers['candidate']; const baseline = solvers['baseline']; + if (candidate === undefined || baseline === undefined) throw new Error('Missing matched implementations.'); + assert.deepEqual({ ...baseline, samplesMs: undefined, timingMs: undefined }, { ...baselineControl, samplesMs: undefined, timingMs: undefined }, 'Baseline changed between calibration and measurement.'); + if (options.baselineSnapshot !== undefined) { + const stored = options.baselineSnapshot.locks.find((row) => row.id === lock.id); + if (stored === undefined || stored.optimumA !== lock.expectedActions) throw new Error(`${lock.id}: baseline snapshot optimum/identity mismatch.`); + assert.deepEqual(baseline.commands, stored.commands, `${lock.id}: target-branch baseline snapshot path drift.`); + assert.deepEqual(commandMetrics(baseline.commands), readMetrics(stored), `${lock.id}: target-branch baseline snapshot metrics drift.`); + } + const quality = compareMetrics(candidate, baseline); + const firstTiming = performanceVerdict(candidate.samplesMs, baseline.samplesMs, selfA, selfB); + const timingAttempts = [{ ...firstTiming, candidate: candidate.samplesMs, baseline: baseline.samplesMs, selfA, selfB }]; + if (firstTiming.verdict !== 'passed' && repetitions >= 5) { + const repeatSamples = new Map(['candidate', 'baseline', 'selfA', 'selfB'].map((name) => [name, []])); + const repeated = [['selfA', options.baseline], ['selfB', options.baseline], ['candidate', options.candidate], ['baseline', options.baseline]] as const; + for (let round = -warmups; round < repetitions; round += 1) { + for (let index = 0; index < repeated.length; index += 1) { + const entry = repeated[(index + round + warmups) % repeated.length]; + if (entry === undefined) throw new Error('Missing repeat entry.'); + const [name, implementation] = entry; + const call = measure(lock, implementation, clock); + const expected: Measurement = name === 'candidate' ? candidate : baseline; + assert.deepEqual(call.solution.commands, expected.commands, `${lock.id}: nondeterministic repeat.`); + if (round >= 0) repeatSamples.get(name)?.push(call.elapsed); + } + } + const candidateRepeat = repeatSamples.get('candidate') ?? []; const baselineRepeat = repeatSamples.get('baseline') ?? []; + const selfARepeat = repeatSamples.get('selfA') ?? []; const selfBRepeat = repeatSamples.get('selfB') ?? []; + timingAttempts.push({ ...performanceVerdict(candidateRepeat, baselineRepeat, selfARepeat, selfBRepeat), + candidate: candidateRepeat, baseline: baselineRepeat, selfA: selfARepeat, selfB: selfBRepeat }); + } + const repeatTiming = timingAttempts[1]; + const timing = { ...firstTiming, verdict: repeatTiming === undefined ? firstTiming.verdict : confirmPerformance(firstTiming.verdict, repeatTiming.verdict), + reason: repeatTiming === undefined ? firstTiming.reason : `First: ${firstTiming.verdict}; bounded repeat: ${repeatTiming.verdict}. Conflicting verdicts remain inconclusive.` }; + options.onProgress?.(lockIndex + 1, locks.length, lock.id); + return { id: lock.id, title: lock.title, optimumA: lock.expectedActions, solvers, quality, timing, + timingAttempts, calibration: { baselineSelfA: selfA, baselineSelfB: selfB }, + historicalReference: { ...lock.historicalReference, timingCompared: false }, + differences: Object.fromEntries(Object.entries(solvers).filter(([name]) => name !== 'candidate').map(([name, other]) => [name, compareMetrics(candidate, other)])) }; + }); + const aggregates = Object.fromEntries(entries.map(([name]) => [name, results.reduce((total, row) => { + const result = row.solvers[name]; if (result === undefined) throw new Error('Missing solver aggregate.'); + return { A: total.A + result.A, U: total.U + result.U, C: total.C + result.C, plateSwitches: total.plateSwitches + result.plateSwitches, + sumOfMediansMs: total.sumOfMediansMs + result.timingMs.median }; + }, { A: 0, U: 0, C: 0, plateSwitches: 0, sumOfMediansMs: 0 })])); + return { schemaVersion: 1, createdAt: new Date().toISOString(), + config: { repetitions, warmups, lockCount: locks.length, scope: locks.length === 45 ? 'full-45-catalog' : 'subset', + timingBoundary: 'Only synchronous public solver call; cloning, legacy/reference normalization, replay and metrics excluded.', + order: 'Per-lock baseline-vs-itself calibration first; matched rounds rotate by lock and round. A non-pass receives one bounded repeat with all raw attempts retained.', + uncertainty: 'Paired log-ratio mean with conservative 2.776 standard-error envelope; control sets tolerance. >=5 samples; >25% control envelope cannot certify a pass.', + gc: 'Natural GC; no forced collection.', fixtureSha256: catalogSha256 }, + correctness: { passed: true }, quality: results.some((row) => row.quality.quality === 'regression') ? 'regression' : 'passed', + performance: combineVerdicts(results.map((row) => row.timing.verdict)), + implementations: Object.fromEntries(entries.map(([name, implementation]) => [name, implementation.label])), aggregates, results }; +} +export type ComparisonReport = ReturnType; +export function renderMarkdown(report: ComparisonReport, metadata: unknown, memory: unknown): string { + const lines = ['# Gothic Lock Solver benchmark evidence', '', `Scope: **${report.config.scope}**; quality: **${report.quality}**; performance: **${report.performance}**.`, '', + `Warmups: ${report.config.warmups}; repetitions: ${report.config.repetitions}. A = grouped actions, U = distinct plates, C = unit divisions, S = plate switches.`, '', + report.config.timingBoundary, report.config.order, report.config.uncertainty, '', + '| Implementation | A | U | C | S | Sum of per-lock medians (ms) |', '| --- | ---: | ---: | ---: | ---: | ---: |']; + for (const [name, metrics] of Object.entries(report.aggregates)) lines.push(`| ${name} | ${metrics.A} | ${metrics.U} | ${metrics.C} | ${metrics.plateSwitches} | ${metrics.sumOfMediansMs.toFixed(3)} |`); + lines.push('', '| Lock | Optimum A | Implementation | A/U/C/S | Median / p95 ms | Quality vs base | Timing vs base |', '| --- | ---: | --- | --- | --- | --- | --- |'); + for (const row of report.results) for (const [name, value] of Object.entries(row.solvers)) lines.push(`| ${row.id} | ${row.optimumA} | ${name} | ${value.A}/${value.U}/${value.C}/${value.plateSwitches} | ${value.timingMs.median.toFixed(3)} / ${value.timingMs.p95.toFixed(3)} | ${name === 'candidate' ? row.quality.quality : ''} | ${name === 'candidate' ? row.timing.verdict : ''} |`); + lines.push('', '## Per-lock candidate differences', '', '| Lock | Comparator | ΔA / ΔU / ΔC / ΔS |', '| --- | --- | --- |'); + for (const row of report.results) for (const [name, difference] of Object.entries(row.differences)) lines.push(`| ${row.id} | ${name} | ${difference.deltas.A}/${difference.deltas.U}/${difference.deltas.C}/${difference.deltas.plateSwitches} |`); + lines.push('', '## Isolated process peak RSS', '', 'RSS includes Node, module loading and runtime allocation; it is not exact algorithm heap allocation. Browser peak memory is unavailable. Memory runs are separate from timed calls.', '', '```json', JSON.stringify(memory, null, 2), '```', '', + '## Environment and source provenance', '', '```json', JSON.stringify(metadata, null, 2), '```', '', + '## Interpretation', '', 'Every warmup and measured solution is replayed independently one unit division at a time, checked against the stored exact action minimum, input immutability and deterministic paths. Target snapshot changes cannot mask candidate drift: the supplied baseline module must reproduce the supplied baseline snapshot.', '', + 'Historical upstream observations have a different objective (unit divisions, then switches); they are never a substitute for live paired timings. U/C/switches are descriptive and are still strict migration regression gates. Timings describe this Node runtime and machine only. A performance inconclusive verdict does not establish absence of regression. Sum of per-lock medians is not a whole-catalog wall-clock median. Raw samples, paths and calibration envelopes are in benchmark.json.', ''); + return lines.join('\n'); +} +export async function publishReport(report: ComparisonReport, outputDir: string, metadata: unknown = {}, memory: unknown = {} ) { + if (!report.correctness.passed) throw new Error('Correctness gates must pass before publishing.'); + const root = resolve(outputDir); await mkdir(root, { recursive: true }); + const staged = await mkdtemp(join(root, '.benchmark-')); + const directory = join(root, `run-${report.createdAt.replaceAll(/[^0-9TZ]/g, '')}-${randomUUID().slice(0, 8)}`); + const full = { ...report, metadata, memory }; + const json = `${JSON.stringify(full, null, 2)}\n`; const markdown = renderMarkdown(report, metadata, memory); + const metadataRecord = record(metadata); + const candidateSource = metadataRecord['candidate'] ?? {}; + const qualitySnapshot = { schemaVersion: 1, fixtureSha256: catalogSha256, source: candidateSource, + reason: 'Measured candidate paths and quality in this immutable benchmark evidence.', + locks: report.results.map((row) => { + const value = row.solvers['candidate']; if (value === undefined) throw new Error('Missing candidate quality snapshot.'); + return { id: row.id, optimumA: row.optimumA, A: value.A, U: value.U, C: value.C, plateSwitches: value.plateSwitches, commands: value.commands }; + }) }; + const evidence = { schemaVersion: 1, createdAt: report.createdAt, quality: report.quality, performance: report.performance, qualitySnapshot, + metadata, memory, config: report.config, aggregates: report.aggregates, + reports: { json: { path: 'benchmark.json', sha256: sha256(json) }, markdown: { path: 'benchmark.md', sha256: sha256(markdown) } } }; + try { + await writeFile(join(staged, 'benchmark.json'), json, { flag: 'wx' }); + await writeFile(join(staged, 'benchmark.md'), markdown, { flag: 'wx' }); + await writeFile(join(staged, 'benchmark-evidence.json'), `${JSON.stringify(evidence, null, 2)}\n`, { flag: 'wx' }); + await rename(staged, directory); + } catch (error) { await rm(staged, { recursive: true, force: true }); throw error; } + return { directory, jsonPath: join(directory, 'benchmark.json'), markdownPath: join(directory, 'benchmark.md'), evidencePath: join(directory, 'benchmark-evidence.json') }; +} diff --git a/benchmarks/memory.ts b/benchmarks/memory.ts new file mode 100644 index 0000000..2f694dc --- /dev/null +++ b/benchmarks/memory.ts @@ -0,0 +1,74 @@ +import { execFileSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { record, string } from './validation.ts'; +import type { Fixture } from './catalog.ts'; +import type { ModuleFormat } from './harness.ts'; +import { confirmPerformance, performanceVerdict, statistics } from './comparison.ts'; +export type MemorySpec = { name: string; module: string; format: ModuleFormat | 'upstream' }; +export type RssSample = { peakRssBytes: number; startupRssBytes: number; loadedRssBytes: number; lockCount: number; peakRssMetric: string; startupPeakRssBytes: number; loadedPeakRssBytes: number; + resourceUsageMaxRssBytes: number; startupResourceUsageMaxRssBytes: number; resourceUsageLaunchFloorDetected: boolean }; +function positive(value: unknown): number { + if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) throw new Error('Invalid worker RSS measurement.'); return value; +} +export function measureRss(spec: MemorySpec, fixtureId?: string): RssSample { + const worker = fileURLToPath(new URL('../scripts/benchmark-memory-worker.ts', import.meta.url)); + const args = [worker, spec.module, spec.format]; if (fixtureId !== undefined) args.push(fixtureId); + const stdout = execFileSync(process.execPath, args, { encoding: 'utf8', timeout: 120_000, maxBuffer: 1_048_576, stdio: ['ignore', 'pipe', 'pipe'] }); + const parsed: unknown = JSON.parse(stdout); const value = record(parsed, 'RSS worker result'); + if (value['status'] !== 'passed') throw new Error(string(value['error'], 'RSS worker error')); + return { peakRssBytes: positive(value['peakRssBytes']), startupRssBytes: positive(value['startupRssBytes']), + loadedRssBytes: positive(value['loadedRssBytes']), lockCount: positive(value['lockCount']), peakRssMetric: string(value['peakRssMetric']), + startupPeakRssBytes: positive(value['startupPeakRssBytes']), loadedPeakRssBytes: positive(value['loadedPeakRssBytes']), + resourceUsageMaxRssBytes: positive(value['resourceUsageMaxRssBytes']), startupResourceUsageMaxRssBytes: positive(value['startupResourceUsageMaxRssBytes']), + resourceUsageLaunchFloorDetected: value['resourceUsageLaunchFloorDetected'] === true }; +} +export function collectMemory(locks: readonly Fixture[], specs: readonly MemorySpec[], repetitions = 5, onProgress?: (text: string) => void) { + const baseline = specs.find((spec) => spec.name === 'baseline'); + if (baseline === undefined) throw new Error('Memory baseline missing.'); + const perLock = locks.map((lock, index) => { + const solvers = Object.fromEntries(specs.map((spec) => [spec.name, measureRss(spec, lock.id)])); + onProgress?.(`RSS ${index + 1}/${locks.length}: ${lock.id}`); + return { id: lock.id, solvers, repetitions: 1, verdict: 'inconclusive', reason: 'Single isolated per-lock observation; environmental variation is not estimated per lock.' }; + }); + const selectedIds = locks.map((lock) => lock.id).join(','); + const groups = [...specs, { ...baseline, name: 'baselineSelfA' }, { ...baseline, name: 'baselineSelfB' }]; + const samples = new Map(groups.map((spec) => [spec.name, []])); + for (let round = 0; round < repetitions; round += 1) { + for (let index = 0; index < groups.length; index += 1) { + const spec = groups[(index + round) % groups.length]; if (spec === undefined) throw new Error('Missing memory implementation.'); + const values = samples.get(spec.name); if (values === undefined) throw new Error('Missing memory series.'); + values.push(measureRss(spec, selectedIds)); + } + onProgress?.(`RSS whole-catalog matched round ${round + 1}/${repetitions}`); + } + const peaks = (name: string): number[] => (samples.get(name) ?? []).map((sample) => sample.peakRssBytes); + const firstVerdict = performanceVerdict(peaks('candidate'), peaks('baseline'), peaks('baselineSelfA'), peaks('baselineSelfB')); + const attempts = [{ ...firstVerdict, samples: Object.fromEntries(samples) }]; + if (firstVerdict.verdict !== 'passed' && repetitions >= 5) { + const retryGroups = groups.filter((spec) => ['candidate', 'baseline', 'baselineSelfA', 'baselineSelfB'].includes(spec.name)); + const retrySamples = new Map(retryGroups.map((spec) => [spec.name, []])); + for (let round = 0; round < repetitions; round += 1) { + for (let index = 0; index < retryGroups.length; index += 1) { + const spec = retryGroups[(index + round) % retryGroups.length]; if (spec === undefined) throw new Error('Missing memory retry implementation.'); + retrySamples.get(spec.name)?.push(measureRss(spec, selectedIds)); + } + onProgress?.(`RSS bounded repeat ${round + 1}/${repetitions}`); + } + const retryPeaks = (name: string): number[] => (retrySamples.get(name) ?? []).map((sample) => sample.peakRssBytes); + attempts.push({ ...performanceVerdict(retryPeaks('candidate'), retryPeaks('baseline'), retryPeaks('baselineSelfA'), retryPeaks('baselineSelfB')), + samples: Object.fromEntries(retrySamples) }); + } + const retry = attempts[1]; + const verdict = { ...firstVerdict, verdict: retry === undefined ? firstVerdict.verdict : confirmPerformance(firstVerdict.verdict, retry.verdict), + reason: retry === undefined ? firstVerdict.reason : `First: ${firstVerdict.verdict}; bounded repeat: ${retry.verdict}. Conflicting observations remain inconclusive.` }; + if ([...samples.values()].flat().some((sample) => sample.peakRssMetric !== 'linux-proc-VmHWM')) { + verdict.verdict = 'inconclusive'; verdict.reason = 'Non-Linux getrusage fallback is recorded but process-image high-water semantics are not verified for this platform.'; + } + return { metric: process.platform === 'linux' ? 'Linux /proc/self/status VmHWM × 1024 bytes (current process-image RSS high-water)' : 'Node process.resourceUsage().maxRSS × 1024 bytes (platform fallback; no certified memory verdict)', repetitions, + sources: ['https://docs.kernel.org/filesystems/proc.html', 'https://nodejs.org/api/process.html#processresourceusage'], + accuracy: 'Kernel RSS accounting is asynchronous and approximate; this is not an exact allocation count. Raw getrusage maxRSS and startup/current-image peaks are preserved to expose pre-exec launch floors.', + scope: `Each per-lock observation is a fresh process. Repeated comparison is peak RSS of the selected ${locks.length}-lock workload per fresh process.`, + includes: 'Node runtime, TypeScript benchmark loader, module loading, fixture loading, normalization, independent replay and solver allocations. Startup and loaded current RSS are recorded separately; subtraction is not exact algorithm allocation.', + browser: { available: false, reason: 'No portable exact browser peak-memory API; Node RSS is not browser memory.' }, + performance: verdict, attempts, wholeCatalog: Object.fromEntries([...samples].map(([name, values]) => [name, { samples: values, peakRssBytes: statistics(values.map((sample) => sample.peakRssBytes)) }])), perLock }; +} diff --git a/benchmarks/reference-matrix/LICENSE b/benchmarks/reference-matrix/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/benchmarks/reference-matrix/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/benchmarks/reference-matrix/manifest.json b/benchmarks/reference-matrix/manifest.json new file mode 100644 index 0000000..c7f090f --- /dev/null +++ b/benchmarks/reference-matrix/manifest.json @@ -0,0 +1,21 @@ +{ + "schemaVersion": 1, + "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", + "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", + "license": "Apache-2.0", + "files": { + "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "src/cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", + "src/index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "src/lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", + "src/matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", + "src/matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", + "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", + "src/search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", + "src/search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", + "src/solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", + "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", + "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" + }, + "notes": "Byte-identical Apache matrix reference, copied from source at the pinned revision. Benchmark-only; excluded from runtime and npm package. No external AGPL solver source." +} diff --git a/src/cli.mjs b/benchmarks/reference-matrix/src/cli.mjs similarity index 100% rename from src/cli.mjs rename to benchmarks/reference-matrix/src/cli.mjs diff --git a/src/index.mjs b/benchmarks/reference-matrix/src/index.mjs similarity index 100% rename from src/index.mjs rename to benchmarks/reference-matrix/src/index.mjs diff --git a/src/lock-definition.mjs b/benchmarks/reference-matrix/src/lock-definition.mjs similarity index 100% rename from src/lock-definition.mjs rename to benchmarks/reference-matrix/src/lock-definition.mjs diff --git a/src/matrix-analysis.mjs b/benchmarks/reference-matrix/src/matrix-analysis.mjs similarity index 100% rename from src/matrix-analysis.mjs rename to benchmarks/reference-matrix/src/matrix-analysis.mjs diff --git a/src/matrix-search.mjs b/benchmarks/reference-matrix/src/matrix-search.mjs similarity index 100% rename from src/matrix-search.mjs rename to benchmarks/reference-matrix/src/matrix-search.mjs diff --git a/src/result.mjs b/benchmarks/reference-matrix/src/result.mjs similarity index 100% rename from src/result.mjs rename to benchmarks/reference-matrix/src/result.mjs diff --git a/src/search-bfs.mjs b/benchmarks/reference-matrix/src/search-bfs.mjs similarity index 100% rename from src/search-bfs.mjs rename to benchmarks/reference-matrix/src/search-bfs.mjs diff --git a/src/search-limits.mjs b/benchmarks/reference-matrix/src/search-limits.mjs similarity index 100% rename from src/search-limits.mjs rename to benchmarks/reference-matrix/src/search-limits.mjs diff --git a/src/solver.mjs b/benchmarks/reference-matrix/src/solver.mjs similarity index 100% rename from src/solver.mjs rename to benchmarks/reference-matrix/src/solver.mjs diff --git a/src/state-codec.mjs b/benchmarks/reference-matrix/src/state-codec.mjs similarity index 100% rename from src/state-codec.mjs rename to benchmarks/reference-matrix/src/state-codec.mjs diff --git a/src/transition.mjs b/benchmarks/reference-matrix/src/transition.mjs similarity index 100% rename from src/transition.mjs rename to benchmarks/reference-matrix/src/transition.mjs diff --git a/benchmarks/rss.ts b/benchmarks/rss.ts new file mode 100644 index 0000000..a1355f9 --- /dev/null +++ b/benchmarks/rss.ts @@ -0,0 +1,14 @@ +import { readFileSync } from 'node:fs'; +export type PeakRss = { peakRssBytes: number; metric: 'linux-proc-VmHWM' | 'node-resourceUsage-maxRSS'; resourceUsageMaxRssBytes: number }; +export function readPeakRss(): PeakRss { + const resourceUsageMaxRssBytes = process.resourceUsage().maxRSS * 1024; + if (process.platform === 'linux') { + // VmHWM belongs to the current process image; getrusage can retain the heavy launcher high-water across exec. + const match = /^VmHWM:\s+(\d+)\s+kB$/m.exec(readFileSync('/proc/self/status', 'utf8')); + const digits = match?.[1]; + const peakRssBytes = digits === undefined ? NaN : Number(digits) * 1024; + if (!Number.isSafeInteger(peakRssBytes) || peakRssBytes <= 0) throw new Error('Linux /proc/self/status has no valid VmHWM RSS observation.'); + return { peakRssBytes, metric: 'linux-proc-VmHWM', resourceUsageMaxRssBytes }; + } + return { peakRssBytes: resourceUsageMaxRssBytes, metric: 'node-resourceUsage-maxRSS', resourceUsageMaxRssBytes }; +} diff --git a/benchmarks/snapshots/reference-matrix.json b/benchmarks/snapshots/reference-matrix.json new file mode 100644 index 0000000..7b9c6ed --- /dev/null +++ b/benchmarks/snapshots/reference-matrix.json @@ -0,0 +1,2394 @@ +{ + "schemaVersion": 1, + "fixtureSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", + "source": { + "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", + "moduleSha256": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "format": "legacy" + }, + "reason": "Initial immutable matrix reference at approved migration boundary; exact A from preserved Apache BFS fixtures.", + "locks": [ + { + "id": "ruined-tower-door", + "optimumA": 17, + "A": 17, + "U": 6, + "C": 57, + "plateSwitches": 16, + "commands": [ + [ + 0, + -4 + ], + [ + 2, + 5 + ], + [ + 3, + -6 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 5, + -2 + ], + [ + 0, + -4 + ], + [ + 1, + 3 + ], + [ + 3, + -5 + ], + [ + 4, + -3 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 1, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "scatty-chest", + "optimumA": 9, + "A": 9, + "U": 5, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + 5 + ], + [ + 4, + 5 + ], + [ + 1, + -1 + ], + [ + 3, + 5 + ], + [ + 4, + 2 + ], + [ + 0, + -2 + ], + [ + 2, + 2 + ], + [ + 4, + 6 + ], + [ + 1, + -3 + ] + ] + }, + { + "id": "cor-galom-chest", + "optimumA": 5, + "A": 5, + "U": 4, + "C": 15, + "plateSwitches": 4, + "commands": [ + [ + 3, + 4 + ], + [ + 1, + -4 + ], + [ + 0, + 4 + ], + [ + 1, + -1 + ], + [ + 2, + -2 + ] + ] + }, + { + "id": "cor-galom-bedroom-chest", + "optimumA": 14, + "A": 14, + "U": 5, + "C": 60, + "plateSwitches": 13, + "commands": [ + [ + 1, + -5 + ], + [ + 2, + -4 + ], + [ + 1, + -4 + ], + [ + 4, + -4 + ], + [ + 1, + -4 + ], + [ + 3, + 4 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 2, + -2 + ], + [ + 1, + -6 + ], + [ + 4, + -6 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "swamp-camp-forge-chest", + "optimumA": 11, + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 2, + 6 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + -5 + ], + [ + 2, + 5 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 1, + -1 + ], + [ + 2, + 5 + ], + [ + 3, + -3 + ], + [ + 4, + -2 + ] + ] + }, + { + "id": "rice-lord-storeroom", + "optimumA": 9, + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 1 + ], + [ + 4, + 3 + ], + [ + 2, + 4 + ], + [ + 1, + 2 + ], + [ + 0, + 3 + ], + [ + 3, + 5 + ], + [ + 4, + 3 + ], + [ + 1, + 6 + ], + [ + 0, + 3 + ] + ] + }, + { + "id": "gomez-chambers-door", + "optimumA": 9, + "A": 9, + "U": 6, + "C": 28, + "plateSwitches": 8, + "commands": [ + [ + 3, + -3 + ], + [ + 2, + 2 + ], + [ + 1, + -2 + ], + [ + 0, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -4 + ], + [ + 5, + -4 + ], + [ + 3, + -5 + ], + [ + 1, + -3 + ] + ] + }, + { + "id": "gomez-throne-hall-chest", + "optimumA": 5, + "A": 5, + "U": 4, + "C": 16, + "plateSwitches": 4, + "commands": [ + [ + 3, + 6 + ], + [ + 0, + 6 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 3, + 1 + ] + ] + }, + { + "id": "old-camp-dungeon-door", + "optimumA": 17, + "A": 17, + "U": 6, + "C": 72, + "plateSwitches": 16, + "commands": [ + [ + 4, + -2 + ], + [ + 1, + -1 + ], + [ + 3, + 2 + ], + [ + 2, + -4 + ], + [ + 5, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + -5 + ], + [ + 5, + -6 + ], + [ + 2, + -6 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 1, + -6 + ], + [ + 0, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ], + [ + 5, + -6 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "arlin-dungeon-key", + "optimumA": 7, + "A": 7, + "U": 5, + "C": 25, + "plateSwitches": 6, + "commands": [ + [ + 2, + -2 + ], + [ + 5, + -2 + ], + [ + 6, + -6 + ], + [ + 0, + -3 + ], + [ + 2, + -6 + ], + [ + 1, + 3 + ], + [ + 5, + -3 + ] + ] + }, + { + "id": "gomez-wine-cellar", + "optimumA": 9, + "A": 9, + "U": 6, + "C": 41, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 2, + 5 + ], + [ + 5, + 4 + ], + [ + 3, + 4 + ], + [ + 2, + -6 + ], + [ + 4, + -5 + ], + [ + 3, + 6 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ] + ] + }, + { + "id": "gomez-bedside-chest", + "optimumA": 23, + "A": 23, + "U": 5, + "C": 68, + "plateSwitches": 22, + "commands": [ + [ + 2, + -1 + ], + [ + 1, + -1 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -4 + ], + [ + 0, + -4 + ], + [ + 1, + -4 + ], + [ + 4, + 2 + ], + [ + 2, + -2 + ], + [ + 3, + -5 + ], + [ + 0, + -4 + ], + [ + 1, + -6 + ], + [ + 4, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + 3 + ], + [ + 1, + -6 + ], + [ + 0, + -3 + ] + ] + }, + { + "id": "gomez-wall-chest", + "optimumA": 20, + "A": 20, + "U": 6, + "C": 75, + "plateSwitches": 19, + "commands": [ + [ + 0, + -2 + ], + [ + 4, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ], + [ + 5, + -6 + ], + [ + 3, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 3, + -3 + ], + [ + 5, + -5 + ], + [ + 0, + -2 + ], + [ + 5, + -2 + ], + [ + 2, + 4 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ], + [ + 1, + -3 + ] + ] + }, + { + "id": "voran-bedside-chest", + "optimumA": 9, + "A": 9, + "U": 7, + "C": 38, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 4, + -1 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 3, + 6 + ], + [ + 5, + 6 + ], + [ + 3, + 6 + ], + [ + 6, + -5 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "arto-room-door", + "optimumA": 7, + "A": 7, + "U": 5, + "C": 31, + "plateSwitches": 6, + "commands": [ + [ + 1, + -6 + ], + [ + 0, + -6 + ], + [ + 1, + -1 + ], + [ + 3, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + 5 + ], + [ + 2, + -3 + ] + ] + }, + { + "id": "arto-far-bed-chest", + "optimumA": 11, + "A": 11, + "U": 5, + "C": 44, + "plateSwitches": 10, + "commands": [ + [ + 1, + 4 + ], + [ + 2, + -2 + ], + [ + 5, + -2 + ], + [ + 3, + 3 + ], + [ + 4, + 6 + ], + [ + 1, + 6 + ], + [ + 5, + -6 + ], + [ + 4, + 6 + ], + [ + 1, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "arto-near-bed-chest", + "optimumA": 13, + "A": 13, + "U": 6, + "C": 52, + "plateSwitches": 12, + "commands": [ + [ + 4, + -1 + ], + [ + 0, + 6 + ], + [ + 5, + -3 + ], + [ + 3, + 4 + ], + [ + 2, + 6 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 3, + 4 + ], + [ + 0, + 6 + ], + [ + 2, + 4 + ], + [ + 5, + -6 + ], + [ + 1, + -3 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "gomez-bedrooms-door", + "optimumA": 14, + "A": 14, + "U": 6, + "C": 49, + "plateSwitches": 13, + "commands": [ + [ + 3, + -6 + ], + [ + 5, + -2 + ], + [ + 2, + 2 + ], + [ + 0, + 3 + ], + [ + 1, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + 3 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 5, + 3 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ] + ] + }, + { + "id": "gomez-bedrooms-far-chest", + "optimumA": 14, + "A": 14, + "U": 5, + "C": 43, + "plateSwitches": 13, + "commands": [ + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 3, + 2 + ], + [ + 4, + -3 + ], + [ + 2, + -4 + ], + [ + 0, + -5 + ], + [ + 1, + -4 + ], + [ + 0, + -2 + ], + [ + 3, + 2 + ], + [ + 2, + -3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "gomez-bedrooms-near-chest", + "optimumA": 12, + "A": 12, + "U": 6, + "C": 56, + "plateSwitches": 11, + "commands": [ + [ + 0, + 4 + ], + [ + 2, + 1 + ], + [ + 5, + -6 + ], + [ + 4, + -6 + ], + [ + 0, + 6 + ], + [ + 5, + -6 + ], + [ + 1, + -6 + ], + [ + 0, + 6 + ], + [ + 3, + -3 + ], + [ + 0, + 3 + ], + [ + 5, + -6 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "raven-tower-left-chest", + "optimumA": 11, + "A": 11, + "U": 6, + "C": 35, + "plateSwitches": 10, + "commands": [ + [ + 4, + 2 + ], + [ + 5, + -3 + ], + [ + 3, + 2 + ], + [ + 0, + 5 + ], + [ + 2, + -4 + ], + [ + 4, + 5 + ], + [ + 5, + -1 + ], + [ + 1, + -3 + ], + [ + 3, + 5 + ], + [ + 0, + 3 + ], + [ + 1, + -2 + ] + ] + }, + { + "id": "raven-tower-right-chest", + "optimumA": 8, + "A": 8, + "U": 6, + "C": 23, + "plateSwitches": 7, + "commands": [ + [ + 1, + 5 + ], + [ + 4, + 1 + ], + [ + 5, + -6 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -2 + ], + [ + 3, + 2 + ], + [ + 5, + -1 + ] + ] + }, + { + "id": "raven-tower-floor2-stairs-chest", + "optimumA": 16, + "A": 16, + "U": 6, + "C": 48, + "plateSwitches": 15, + "commands": [ + [ + 5, + 3 + ], + [ + 0, + -1 + ], + [ + 4, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + 4 + ], + [ + 3, + -2 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 1, + 6 + ], + [ + 2, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "raven-tower-floor2-chest-2", + "optimumA": 8, + "A": 8, + "U": 5, + "C": 29, + "plateSwitches": 7, + "commands": [ + [ + 3, + 1 + ], + [ + 2, + -5 + ], + [ + 4, + 5 + ], + [ + 3, + -3 + ], + [ + 0, + -1 + ], + [ + 1, + 5 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ] + ] + }, + { + "id": "raven-tower-floor3-entrance-chest", + "optimumA": 14, + "A": 14, + "U": 5, + "C": 44, + "plateSwitches": 13, + "commands": [ + [ + 3, + -4 + ], + [ + 1, + 1 + ], + [ + 4, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + 2 + ], + [ + 4, + -4 + ], + [ + 2, + -4 + ], + [ + 1, + 3 + ], + [ + 3, + -6 + ], + [ + 4, + -3 + ], + [ + 0, + -3 + ], + [ + 1, + 3 + ], + [ + 4, + -5 + ], + [ + 0, + -2 + ] + ] + }, + { + "id": "raven-tower-floor3-empty-chest", + "optimumA": 9, + "A": 9, + "U": 5, + "C": 37, + "plateSwitches": 8, + "commands": [ + [ + 0, + -2 + ], + [ + 4, + 6 + ], + [ + 2, + 3 + ], + [ + 1, + -3 + ], + [ + 4, + 6 + ], + [ + 3, + -4 + ], + [ + 0, + -6 + ], + [ + 4, + 4 + ], + [ + 2, + 3 + ] + ] + }, + { + "id": "raven-tower-floor4-elixirs-chest", + "optimumA": 8, + "A": 8, + "U": 5, + "C": 36, + "plateSwitches": 7, + "commands": [ + [ + 1, + -5 + ], + [ + 2, + -5 + ], + [ + 1, + -5 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ], + [ + 2, + -6 + ], + [ + 1, + -3 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "raven-tower-floor4-food-chest", + "optimumA": 11, + "A": 11, + "U": 6, + "C": 43, + "plateSwitches": 10, + "commands": [ + [ + 2, + 4 + ], + [ + 0, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 6 + ], + [ + 0, + 5 + ], + [ + 3, + -1 + ], + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + 2 + ], + [ + 5, + -3 + ] + ] + }, + { + "id": "raven-tower-top-chest", + "optimumA": 20, + "A": 20, + "U": 6, + "C": 48, + "plateSwitches": 19, + "commands": [ + [ + 1, + -3 + ], + [ + 3, + 1 + ], + [ + 0, + -1 + ], + [ + 4, + -2 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 3, + 2 + ], + [ + 5, + -4 + ], + [ + 2, + -1 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + -6 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "fire-lizard-cave-chest", + "optimumA": 10, + "A": 10, + "U": 6, + "C": 41, + "plateSwitches": 9, + "commands": [ + [ + 0, + -3 + ], + [ + 2, + -5 + ], + [ + 4, + 4 + ], + [ + 1, + 3 + ], + [ + 5, + -5 + ], + [ + 0, + -6 + ], + [ + 3, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + 6 + ], + [ + 0, + -3 + ] + ] + }, + { + "id": "silas-tavern-basement-chest", + "optimumA": 9, + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 4 + ], + [ + 3, + -3 + ], + [ + 1, + -4 + ], + [ + 3, + -3 + ], + [ + 4, + 6 + ], + [ + 2, + 3 + ], + [ + 3, + -3 + ] + ] + }, + { + "id": "silas-tavern-storeroom-chest", + "optimumA": 9, + "A": 9, + "U": 6, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + -3 + ], + [ + 3, + 4 + ], + [ + 5, + -6 + ], + [ + 0, + 2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -6 + ], + [ + 3, + 3 + ], + [ + 5, + -3 + ] + ] + }, + { + "id": "silas-tavern-storeroom-door", + "optimumA": 10, + "A": 10, + "U": 5, + "C": 31, + "plateSwitches": 9, + "commands": [ + [ + 3, + 2 + ], + [ + 5, + -3 + ], + [ + 1, + -3 + ], + [ + 5, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + 3 + ], + [ + 5, + -3 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 2, + -3 + ] + ] + }, + { + "id": "jan-pond-house-chest", + "optimumA": 8, + "A": 8, + "U": 6, + "C": 33, + "plateSwitches": 7, + "commands": [ + [ + 0, + -4 + ], + [ + 2, + -6 + ], + [ + 3, + 4 + ], + [ + 4, + 3 + ], + [ + 5, + -3 + ], + [ + 3, + 5 + ], + [ + 5, + -5 + ], + [ + 1, + 3 + ] + ] + }, + { + "id": "graham-chest", + "optimumA": 10, + "A": 10, + "U": 6, + "C": 40, + "plateSwitches": 9, + "commands": [ + [ + 1, + 5 + ], + [ + 4, + 6 + ], + [ + 0, + -5 + ], + [ + 1, + 6 + ], + [ + 4, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 2 + ], + [ + 3, + -3 + ], + [ + 4, + 2 + ], + [ + 5, + -1 + ] + ] + }, + { + "id": "gravo-chest", + "optimumA": 8, + "A": 8, + "U": 5, + "C": 27, + "plateSwitches": 7, + "commands": [ + [ + 0, + -5 + ], + [ + 4, + -1 + ], + [ + 1, + 3 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + -6 + ], + [ + 1, + 3 + ] + ] + }, + { + "id": "diego-chest", + "optimumA": 11, + "A": 11, + "U": 6, + "C": 40, + "plateSwitches": 10, + "commands": [ + [ + 0, + 2 + ], + [ + 1, + 1 + ], + [ + 2, + -6 + ], + [ + 3, + -1 + ], + [ + 1, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + -6 + ], + [ + 5, + 3 + ], + [ + 1, + 6 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ] + ] + }, + { + "id": "grim-chest", + "optimumA": 8, + "A": 8, + "U": 6, + "C": 21, + "plateSwitches": 7, + "commands": [ + [ + 4, + -2 + ], + [ + 3, + 1 + ], + [ + 1, + 6 + ], + [ + 2, + -1 + ], + [ + 0, + -5 + ], + [ + 3, + 4 + ], + [ + 0, + -1 + ], + [ + 5, + -1 + ] + ] + }, + { + "id": "mercenary-near-grim-chest", + "optimumA": 8, + "A": 8, + "U": 6, + "C": 25, + "plateSwitches": 7, + "commands": [ + [ + 2, + 3 + ], + [ + 1, + 3 + ], + [ + 3, + -6 + ], + [ + 5, + 6 + ], + [ + 0, + -2 + ], + [ + 3, + -2 + ], + [ + 4, + 2 + ], + [ + 5, + 1 + ] + ] + }, + { + "id": "popular-lock", + "optimumA": 13, + "A": 13, + "U": 5, + "C": 46, + "plateSwitches": 12, + "commands": [ + [ + 4, + -4 + ], + [ + 2, + -1 + ], + [ + 3, + -2 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + 6 + ], + [ + 2, + -3 + ], + [ + 4, + -6 + ], + [ + 1, + -3 + ], + [ + 0, + 3 + ], + [ + 3, + -3 + ], + [ + 4, + -6 + ], + [ + 0, + 3 + ] + ] + }, + { + "id": "alberto-mine-hut-chest", + "optimumA": 9, + "A": 9, + "U": 5, + "C": 36, + "plateSwitches": 8, + "commands": [ + [ + 3, + -4 + ], + [ + 1, + -2 + ], + [ + 4, + 6 + ], + [ + 0, + -3 + ], + [ + 3, + -6 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + 3 + ] + ] + }, + { + "id": "alberto-mine-upstairs-left-chest", + "optimumA": 4, + "A": 4, + "U": 3, + "C": 20, + "plateSwitches": 3, + "commands": [ + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 1, + 6 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "alberto-mine-upstairs-right-chest", + "optimumA": 5, + "A": 5, + "U": 5, + "C": 13, + "plateSwitches": 4, + "commands": [ + [ + 0, + 3 + ], + [ + 1, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 4 + ], + [ + 3, + 2 + ] + ] + }, + { + "id": "cavalorn-storeroom-door", + "optimumA": 11, + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 3, + -2 + ], + [ + 5, + 4 + ], + [ + 0, + -1 + ], + [ + 1, + -6 + ], + [ + 0, + -4 + ], + [ + 3, + -2 + ], + [ + 1, + -6 + ], + [ + 4, + -4 + ], + [ + 5, + 6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ] + ] + }, + { + "id": "old-to-new-camp-bandits-chest", + "optimumA": 10, + "A": 10, + "U": 5, + "C": 33, + "plateSwitches": 9, + "commands": [ + [ + 4, + 6 + ], + [ + 5, + -2 + ], + [ + 2, + -1 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ] + ] + } + ] +} diff --git a/benchmarks/solver.bench.ts b/benchmarks/solver.bench.ts new file mode 100644 index 0000000..390add8 --- /dev/null +++ b/benchmarks/solver.bench.ts @@ -0,0 +1,24 @@ +import { test } from 'vitest'; +import { loadCatalog } from './catalog.ts'; +import { loadImplementation } from './harness.ts'; +import { replayCommands } from './validation.ts'; +import { fileURLToPath } from 'node:url'; + +const candidate = await loadImplementation(fileURLToPath(new URL('../src/index.ts', import.meta.url)), 'tuple', 'Developer source'); +const baseline = await loadImplementation(fileURLToPath(new URL('./reference-matrix/src/index.mjs', import.meta.url)), 'legacy', 'Pinned matrix'); +const locks = loadCatalog(); +// One representative from each encountered plate count, selected deterministically from the fixed catalog. +const representatives = [...new Map(locks.map((lock) => [lock.definition.state.length, lock])).values()]; +for (const lock of representatives) { + for (const solver of [candidate, baseline]) { + const commands = solver.normalize(solver.solve(structuredClone(lock.definition))); + if (commands === null || replayCommands(lock.definition, commands).some((position) => position !== 4)) throw new Error('Developer benchmark setup did not reach target.'); + } + test(`${lock.id} (${lock.definition.state.length} plates)`, async ({ bench }) => { + await bench.compare( + bench('candidate tuple API', () => { candidate.solve(lock.definition); }), + bench('pinned legacy matrix API', () => { baseline.solve(lock.definition); }), + { iterations: 10, time: 100 }, + ); + }); +} diff --git a/benchmarks/validation.mjs b/benchmarks/validation.mjs deleted file mode 100644 index 27f658d..0000000 --- a/benchmarks/validation.mjs +++ /dev/null @@ -1,54 +0,0 @@ -// Independent verification: deliberately imports none of the production transition/search code. -export function replayCommands({ state, links }, commands) { - if (!Array.isArray(commands)) throw new Error('Commands must be an array.'); - let current = [...state]; - for (const [index, command] of commands.entries()) { - if (!command || !Number.isInteger(command.plate) || command.plate < 1 - || command.plate > current.length || !['left', 'right'].includes(command.direction) - || !Number.isInteger(command.steps) || command.steps < 1 || command.steps > 6) { - throw new Error(`Invalid command at index ${index}.`); - } - const source = command.plate - 1; - const sign = command.direction === 'left' ? 1 : -1; - // Every unit click is simultaneous. Only the selected plate's outgoing row is read. - for (let click = 0; click < command.steps; click += 1) { - const next = current.map((position, target) => position - + sign * (target === source ? 1 : links[source][target])); - if (next.some((position) => !Number.isInteger(position) || position < 1 || position > 7)) { - throw new Error(`Blocked intermediate click ${click + 1} of command ${index + 1}.`); - } - current = next; - } - } - return current; -} - -export function commandMetrics(commands) { - return { - A: commands.length, - U: new Set(commands.map((command) => command.plate)).size, - C: commands.reduce((sum, command) => sum + command.steps, 0), - plateSwitches: commands.reduce((sum, command, index) => sum - + Number(index > 0 && commands[index - 1].plate !== command.plate), 0), - }; -} - -export function groupReferenceClicks(sequence) { - if (sequence === null) return null; - if (!Array.isArray(sequence)) throw new Error('Invalid reference result.'); - const commands = []; - for (const click of sequence) { - if (!click || !Number.isInteger(click.plate) || click.plate < 0 - || (click.d !== 1 && click.d !== -1)) { - throw new Error('Invalid reference unit click.'); - } - const direction = click.d === 1 ? 'left' : 'right'; - const last = commands.at(-1); - if (last?.plate === click.plate + 1 && last.direction === direction) { - last.steps += 1; - } else { - commands.push({ plate: click.plate + 1, direction, steps: 1 }); - } - } - return commands; -} diff --git a/benchmarks/validation.ts b/benchmarks/validation.ts new file mode 100644 index 0000000..c88cb46 --- /dev/null +++ b/benchmarks/validation.ts @@ -0,0 +1,78 @@ +// Deliberately independent of production types, transitions, encoders and search. +export type TupleCommand = readonly [index: number, delta: number]; +export type Definition = { state: number[]; links: number[][] }; +export type Metrics = { A: number; U: number; C: number; plateSwitches: number }; + +export function record(value: unknown, label = 'value'): Record { + if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error(`Invalid ${label}: expected object.`); + // Object.entries exposes own properties only; no unchecked structural assertion. + return Object.fromEntries(Object.entries(value)); +} +export function array(value: unknown, label = 'value'): unknown[] { + if (!Array.isArray(value)) throw new Error(`Invalid ${label}: expected array.`); + return Array.from(value, (item: unknown) => item); +} +export function integer(value: unknown, label = 'value'): number { + if (typeof value !== 'number' || !Number.isSafeInteger(value)) throw new Error(`Invalid ${label}: expected integer.`); + return value; +} +export function string(value: unknown, label = 'value'): string { + if (typeof value !== 'string') throw new Error(`Invalid ${label}: expected string.`); + return value; +} +export function tupleCommands(value: unknown): TupleCommand[] { + return array(value, 'commands').map((command) => { + const tuple = array(command, 'command'); + const index = integer(tuple[0], 'command index'); + const delta = integer(tuple[1], 'command delta'); + if (tuple.length !== 2 || index < 0 || delta === 0 || Math.abs(delta) > 6) throw new Error('Invalid command coordinates.'); + return [index, delta]; + }); +} +export function legacyCommands(value: unknown): TupleCommand[] { + return array(value, 'legacy commands').map((command) => { + const item = record(command, 'legacy command'); + const plate = integer(item['plate'], 'legacy command plate'); + const steps = integer(item['steps'], 'legacy command steps'); + const direction = item['direction']; + if (plate < 1 || steps < 1 || steps > 6 || (direction !== 'left' && direction !== 'right')) throw new Error('Invalid legacy command.'); + return [plate - 1, direction === 'left' ? steps : -steps]; + }); +} +export function groupReferenceClicks(value: unknown): TupleCommand[] | null { + if (value === null) return null; + const commands: [number, number][] = []; + for (const raw of array(value, 'reference result')) { + const click = record(raw, 'reference click'); + const index = integer(click['plate'], 'reference plate'); + const delta = integer(click['d'], 'reference delta'); + if (index < 0 || Math.abs(delta) !== 1) throw new Error('Invalid reference unit click.'); + const last = commands.at(-1); + if (last !== undefined && last[0] === index && Math.sign(last[1]) === delta) last[1] += delta; + else commands.push([index, delta]); + } + return tupleCommands(commands); +} +export function replayCommands(definition: Definition, value: unknown): number[] { + let state = [...definition.state]; + for (const [index, delta] of tupleCommands(value)) { + const row = definition.links[index]; + if (index >= state.length || row === undefined) throw new Error('Invalid command plate index.'); + for (let click = 0; click < Math.abs(delta); click += 1) { + const next = state.map((position, target) => { + const link = row[target]; + if (link === undefined) throw new Error('Invalid link matrix.'); + return position + Math.sign(delta) * (target === index ? 1 : link); + }); + if (next.some((position) => position < 1 || position > 7 || !Number.isInteger(position))) throw new Error('Blocked intermediate command click.'); + state = next; + } + } + return state; +} +export function commandMetrics(value: unknown): Metrics { + const commands = tupleCommands(value); + return { A: commands.length, U: new Set(commands.map(([index]) => index)).size, + C: commands.reduce((sum, [, delta]) => sum + Math.abs(delta), 0), + plateSwitches: commands.reduce((sum, [index], position) => sum + Number(position > 0 && commands[position - 1]?.[0] !== index), 0) }; +} diff --git a/cli/cli.ts b/cli/cli.ts new file mode 100644 index 0000000..873a8d2 --- /dev/null +++ b/cli/cli.ts @@ -0,0 +1,102 @@ +import { readFile, writeFile } from 'node:fs/promises'; +import { solveLock } from '../src/index.ts'; +import { LockModel } from '../src/lock-model.ts'; +import type { Command, Position } from '../src/types.ts'; + +interface TextWriter { + write(text: string): unknown; +} + +interface Streams { + readonly stdout: TextWriter; + readonly stderr: TextWriter; +} + +interface LegacyCommand { + readonly plate: number; + readonly direction: 'left' | 'right'; + readonly steps: number; +} + +interface ResultBase { + readonly initialState: readonly Position[]; + readonly targetState: readonly Position[]; + readonly commands: readonly LegacyCommand[]; +} + +type CliResult = ResultBase & ( + | { readonly status: 'unsolvable' } + | { + readonly status: 'solved'; + readonly finalState: readonly Position[]; + readonly metrics: { readonly commands: number; readonly divisions: number; readonly plateSwitches: number }; + } +); + +function parseArgs(argv: readonly string[]): { inputPath: string; outputPath: string | null } { + const [input, flag, output] = argv; + if (typeof input === 'string' && input.length > 0 && !input.startsWith('--')) { + if (argv.length === 1) return { inputPath: input, outputPath: null }; + if (argv.length === 3 && flag === '--output' && typeof output === 'string' && output.length > 0 && !output.startsWith('--')) { + return { inputPath: input, outputPath: output }; + } + } + throw new Error('Использование: gothic-lock-solver [--output ]'); +} + +function parseInput(json: string): LockModel { + let value: unknown; + try { + value = JSON.parse(json); + } catch { + throw new Error('Некорректный JSON.'); + } + if (typeof value !== 'object' || value === null || Array.isArray(value) || !('state' in value) || !('links' in value)) { + throw new Error('Ожидается объект с полями state и links.'); + } + return new LockModel(value.state, value.links); +} + +/** Preserve the file/console adapter's historical one-based, physical-direction notation. */ +function createResult(state: readonly Position[], solution: readonly Command[] | null): CliResult { + const initialState = [...state]; + const targetState = state.map((): Position => 4); + if (solution === null) return { status: 'unsolvable', initialState, targetState, commands: [] }; + let previous: number | undefined; + let divisions = 0; + let plateSwitches = 0; + const commands: LegacyCommand[] = []; + for (const [index, delta] of solution) { + if (previous !== undefined && previous !== index) plateSwitches += 1; + previous = index; + divisions += Math.abs(delta); + commands.push({ plate: index + 1, direction: delta > 0 ? 'left' : 'right', steps: Math.abs(delta) }); + } + return { + status: 'solved', initialState, targetState, commands, finalState: [...targetState], + metrics: { commands: commands.length, divisions, plateSwitches }, + }; +} + +function formatResult(result: CliResult): string { + if (result.status === 'unsolvable') return 'Решение не найдено.'; + if (result.commands.length === 0) return 'Замок уже открыт.'; + const lines = result.commands.map((command, index) => + `${index + 1}. Пластина ${command.plate} - ${command.direction === 'left' ? 'влево' : 'вправо'} x${command.steps}`); + return [`Найдено команд: ${result.commands.length}`, ...lines].join('\n'); +} + +/** Exit status: 0 solved, 2 proved unreachable, 1 malformed input/IO/resource failure. */ +export async function runCli(argv: readonly string[], streams: Streams = process): Promise<0 | 1 | 2> { + try { + const { inputPath, outputPath } = parseArgs(argv); + const model = parseInput(await readFile(inputPath, 'utf8')); + const result = createResult(model.state, solveLock(model.state, model.links)); + if (outputPath !== null) await writeFile(outputPath, `${JSON.stringify(result, null, 2)}\n`, 'utf8'); + streams.stdout.write(`${formatResult(result)}\n`); + return result.status === 'unsolvable' ? 2 : 0; + } catch (error) { + streams.stderr.write(`Ошибка: ${error instanceof Error ? error.message : String(error)}\n`); + return 1; + } +} diff --git a/cli/main.ts b/cli/main.ts new file mode 100644 index 0000000..17719ad --- /dev/null +++ b/cli/main.ts @@ -0,0 +1,4 @@ +#!/usr/bin/env node +import { runCli } from './cli.ts'; + +process.exitCode = await runCli(process.argv.slice(2)); diff --git a/dist/gothic-lock-solver.js b/dist/gothic-lock-solver.js deleted file mode 100644 index b520b1e..0000000 --- a/dist/gothic-lock-solver.js +++ /dev/null @@ -1,912 +0,0 @@ -/*! - * gothic-lock-solver — standalone classic browser script - * Project attribution: https://github.com/Marcus-Rise/gothic-lock-solver - * SPDX-License-Identifier: Apache-2.0 - * Generated by scripts/build-browser.mjs. Do not edit this artifact directly. - * Source SHA-256 (ordered UTF-8/LF modules and LICENSE): be3b68485e67d47a855cf6f3103917676234a5f53ca6227e7e7fd1d3a656bcdf - * No runtime dependencies, module imports, network access, or generated code. - * - * Apache License - * Version 2.0, January 2004 - * http://www.apache.org/licenses/ - * - * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - * - * 1. Definitions. - * - * "License" shall mean the terms and conditions for use, reproduction, - * and distribution as defined by Sections 1 through 9 of this document. - * - * "Licensor" shall mean the copyright owner or entity authorized by - * the copyright owner that is granting the License. - * - * "Legal Entity" shall mean the union of the acting entity and all - * other entities that control, are controlled by, or are under common - * control with that entity. For the purposes of this definition, - * "control" means (i) the power, direct or indirect, to cause the - * direction or management of such entity, whether by contract or - * otherwise, or (ii) ownership of fifty percent (50%) or more of the - * outstanding shares, or (iii) beneficial ownership of such entity. - * - * "You" (or "Your") shall mean an individual or Legal Entity - * exercising permissions granted by this License. - * - * "Source" form shall mean the preferred form for making modifications, - * including but not limited to software source code, documentation - * source, and configuration files. - * - * "Object" form shall mean any form resulting from mechanical - * transformation or translation of a Source form, including but - * not limited to compiled object code, generated documentation, - * and conversions to other media types. - * - * "Work" shall mean the work of authorship, whether in Source or - * Object form, made available under the License, as indicated by a - * copyright notice that is included in or attached to the work - * (an example is provided in the Appendix below). - * - * "Derivative Works" shall mean any work, whether in Source or Object - * form, that is based on (or derived from) the Work and for which the - * editorial revisions, annotations, elaborations, or other modifications - * represent, as a whole, an original work of authorship. For the purposes - * of this License, Derivative Works shall not include works that remain - * separable from, or merely link (or bind by name) to the interfaces of, - * the Work and Derivative Works thereof. - * - * "Contribution" shall mean any work of authorship, including - * the original version of the Work and any modifications or additions - * to that Work or Derivative Works thereof, that is intentionally - * submitted to Licensor for inclusion in the Work by the copyright owner - * or by an individual or Legal Entity authorized to submit on behalf of - * the copyright owner. For the purposes of this definition, "submitted" - * means any form of electronic, verbal, or written communication sent - * to the Licensor or its representatives, including but not limited to - * communication on electronic mailing lists, source code control systems, - * and issue tracking systems that are managed by, or on behalf of, the - * Licensor for the purpose of discussing and improving the Work, but - * excluding communication that is conspicuously marked or otherwise - * designated in writing by the copyright owner as "Not a Contribution." - * - * "Contributor" shall mean Licensor and any individual or Legal Entity - * on behalf of whom a Contribution has been received by Licensor and - * subsequently incorporated within the Work. - * - * 2. Grant of Copyright License. Subject to the terms and conditions of - * this License, each Contributor hereby grants to You a perpetual, - * worldwide, non-exclusive, no-charge, royalty-free, irrevocable - * copyright license to reproduce, prepare Derivative Works of, - * publicly display, publicly perform, sublicense, and distribute the - * Work and such Derivative Works in Source or Object form. - * - * 3. Grant of Patent License. Subject to the terms and conditions of - * this License, each Contributor hereby grants to You a perpetual, - * worldwide, non-exclusive, no-charge, royalty-free, irrevocable - * (except as stated in this section) patent license to make, have made, - * use, offer to sell, sell, import, and otherwise transfer the Work, - * where such license applies only to those patent claims licensable - * by such Contributor that are necessarily infringed by their - * Contribution(s) alone or by combination of their Contribution(s) - * with the Work to which such Contribution(s) was submitted. If You - * institute patent litigation against any entity (including a - * cross-claim or counterclaim in a lawsuit) alleging that the Work - * or a Contribution incorporated within the Work constitutes direct - * or contributory patent infringement, then any patent licenses - * granted to You under this License for that Work shall terminate - * as of the date such litigation is filed. - * - * 4. Redistribution. You may reproduce and distribute copies of the - * Work or Derivative Works thereof in any medium, with or without - * modifications, and in Source or Object form, provided that You - * meet the following conditions: - * - * (a) You must give any other recipients of the Work or - * Derivative Works a copy of this License; and - * - * (b) You must cause any modified files to carry prominent notices - * stating that You changed the files; and - * - * (c) You must retain, in the Source form of any Derivative Works - * that You distribute, all copyright, patent, trademark, and - * attribution notices from the Source form of the Work, - * excluding those notices that do not pertain to any part of - * the Derivative Works; and - * - * (d) If the Work includes a "NOTICE" text file as part of its - * distribution, then any Derivative Works that You distribute must - * include a readable copy of the attribution notices contained - * within such NOTICE file, excluding those notices that do not - * pertain to any part of the Derivative Works, in at least one - * of the following places: within a NOTICE text file distributed - * as part of the Derivative Works; within the Source form or - * documentation, if provided along with the Derivative Works; or, - * within a display generated by the Derivative Works, if and - * wherever such third-party notices normally appear. The contents - * of the NOTICE file are for informational purposes only and - * do not modify the License. You may add Your own attribution - * notices within Derivative Works that You distribute, alongside - * or as an addendum to the NOTICE text from the Work, provided - * that such additional attribution notices cannot be construed - * as modifying the License. - * - * You may add Your own copyright statement to Your modifications and - * may provide additional or different license terms and conditions - * for use, reproduction, or distribution of Your modifications, or - * for any such Derivative Works as a whole, provided Your use, - * reproduction, and distribution of the Work otherwise complies with - * the conditions stated in this License. - * - * 5. Submission of Contributions. Unless You explicitly state otherwise, - * any Contribution intentionally submitted for inclusion in the Work - * by You to the Licensor shall be under the terms and conditions of - * this License, without any additional terms or conditions. - * Notwithstanding the above, nothing herein shall supersede or modify - * the terms of any separate license agreement you may have executed - * with Licensor regarding such Contributions. - * - * 6. Trademarks. This License does not grant permission to use the trade - * names, trademarks, service marks, or product names of the Licensor, - * except as required for reasonable and customary use in describing the - * origin of the Work and reproducing the content of the NOTICE file. - * - * 7. Disclaimer of Warranty. Unless required by applicable law or - * agreed to in writing, Licensor provides the Work (and each - * Contributor provides its Contributions) on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - * implied, including, without limitation, any warranties or conditions - * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - * PARTICULAR PURPOSE. You are solely responsible for determining the - * appropriateness of using or redistributing the Work and assume any - * risks associated with Your exercise of permissions under this License. - * - * 8. Limitation of Liability. In no event and under no legal theory, - * whether in tort (including negligence), contract, or otherwise, - * unless required by applicable law (such as deliberate and grossly - * negligent acts) or agreed to in writing, shall any Contributor be - * liable to You for damages, including any direct, indirect, special, - * incidental, or consequential damages of any character arising as a - * result of this License or out of the use or inability to use the - * Work (including but not limited to damages for loss of goodwill, - * work stoppage, computer failure or malfunction, or any and all - * other commercial damages or losses), even if such Contributor - * has been advised of the possibility of such damages. - * - * 9. Accepting Warranty or Additional Liability. While redistributing - * the Work or Derivative Works thereof, You may choose to offer, - * and charge a fee for, acceptance of support, warranty, indemnity, - * or other liability obligations and/or rights consistent with this - * License. However, in accepting such obligations, You may act only - * on Your own behalf and on Your sole responsibility, not on behalf - * of any other Contributor, and only if You agree to indemnify, - * defend, and hold each Contributor harmless for any liability - * incurred by, or claims asserted against, such Contributor by reason - * of your accepting any such warranty or additional liability. - * - * END OF TERMS AND CONDITIONS - * - * APPENDIX: How to apply the Apache License to your work. - * - * To apply the Apache License to your work, attach the following - * boilerplate notice, with the fields enclosed by brackets "[]" - * replaced with your own identifying information. (Don't include - * the brackets!) The text should be enclosed in the appropriate - * comment syntax for the file format. We also recommend that a - * file or class name and description of purpose be included on the - * same "printed page" as the copyright notice for easier - * identification within third-party archives. - * - * Copyright [yyyy] [name of copyright owner] - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -(function () { -'use strict'; -const modules = Object.create(null); - -// src/lock-definition.mjs -modules["src/lock-definition.mjs"] = (() => { -class LockInputError extends Error { - constructor(message, options) { - super(message, options); - this.name = 'LockInputError'; - } -} - -function validateLockDefinition(value) { - if (value === null || typeof value !== 'object' || Array.isArray(value)) { - throw new LockInputError('Определение замка должно быть JSON-объектом.'); - } - - const { state, links } = value; - if (!Array.isArray(state) || state.length < 2) { - throw new LockInputError('state должен содержать не менее 2 пластин.'); - } - - for (let index = 0; index < state.length; index += 1) { - const position = state[index]; - if (!Object.hasOwn(state, index) || !Number.isInteger(position) || position < 1 || position > 7) { - throw new LockInputError(`state[${index}] должен быть целым числом от 1 до 7.`); - } - } - - if (!Array.isArray(links) || links.length !== state.length) { - throw new LockInputError(`links должен быть матрицей ${state.length}x${state.length}.`); - } - - for (let rowIndex = 0; rowIndex < links.length; rowIndex += 1) { - const row = links[rowIndex]; - if (!Object.hasOwn(links, rowIndex) || !Array.isArray(row) || row.length !== state.length) { - throw new LockInputError(`links[${rowIndex}] должен содержать ${state.length} элементов.`); - } - for (let columnIndex = 0; columnIndex < row.length; columnIndex += 1) { - const coefficient = row[columnIndex]; - if (!Object.hasOwn(row, columnIndex) || ![-1, 0, 1].includes(coefficient)) { - throw new LockInputError(`links[${rowIndex}][${columnIndex}] должен быть -1, 0 или 1.`); - } - if (rowIndex === columnIndex && coefficient !== 0) { - throw new LockInputError(`links[${rowIndex}][${columnIndex}] на диагонали должен быть 0.`); - } - } - } - - return { - state: [...state], - links: links.map((row) => [...row]), - }; -} - -function parseLockDefinition(text) { - try { - return validateLockDefinition(JSON.parse(text)); - } catch (error) { - if (error instanceof LockInputError) { - throw error; - } - throw new LockInputError(`Некорректный JSON: ${error.message}`, { cause: error }); - } -} - -return Object.freeze({ LockInputError, validateLockDefinition, parseLockDefinition }); -})(); - -// src/state-codec.mjs -modules["src/state-codec.mjs"] = (() => { -function stateSpaceSize(plateCount) { - return 7 ** plateCount; -} - -function encodeState(state) { - let code = 0; - for (const position of state) { - code = code * 7 + (position - 1); - } - return code; -} - -function decodeState(code, plateCount) { - const state = new Array(plateCount); - let remainder = code; - for (let index = plateCount - 1; index >= 0; index -= 1) { - state[index] = (remainder % 7) + 1; - remainder = Math.floor(remainder / 7); - } - return state; -} - -function createTargetState(plateCount) { - return Array.from({ length: plateCount }, () => 4); -} - -return Object.freeze({ stateSpaceSize, encodeState, decodeState, createTargetState }); -})(); - -// src/transition.mjs -modules["src/transition.mjs"] = (() => { -const DIRECTIONS = ['left', 'right']; - -function applyCommand(state, links, command) { - const sourceIndex = command.plate - 1; - const directionDelta = command.direction === 'left' ? 1 : -1; - const nextState = [...state]; - - for (let targetIndex = 0; targetIndex < state.length; targetIndex += 1) { - const coefficient = targetIndex === sourceIndex - ? 1 - : links[sourceIndex][targetIndex]; - - if (coefficient === 0) { - continue; - } - - const nextPosition = state[targetIndex] - + directionDelta * coefficient * command.steps; - if (nextPosition < 1 || nextPosition > 7) { - return null; - } - nextState[targetIndex] = nextPosition; - } - - return nextState; -} - -function generateTransitions(state, links) { - const transitions = []; - for (let plate = 1; plate <= state.length; plate += 1) { - const sourcePosition = state[plate - 1]; - for (const direction of DIRECTIONS) { - const sourceLimit = direction === 'left' - ? 7 - sourcePosition - : sourcePosition - 1; - for (let steps = sourceLimit; steps >= 1; steps -= 1) { - const command = { plate, direction, steps }; - const nextState = applyCommand(state, links, command); - if (nextState !== null) { - transitions.push({ command, state: nextState }); - } - } - } - } - return transitions; -} - -return Object.freeze({ applyCommand, generateTransitions }); -})(); - -// src/result.mjs -modules["src/result.mjs"] = (() => { -const { createTargetState } = modules["src/state-codec.mjs"]; -const { applyCommand } = modules["src/transition.mjs"]; - -function createSolveResult(definition, commands) { - const initialState = [...definition.state]; - const targetState = createTargetState(initialState.length); - - if (commands === null) { - return { status: 'unsolvable', initialState, targetState, commands: [] }; - } - - let finalState = [...initialState]; - for (const command of commands) { - const nextState = applyCommand(finalState, definition.links, command); - if (nextState === null) { - throw new Error('Решатель вернул заблокированную команду.'); - } - finalState = nextState; - } - - if (!finalState.every((position) => position === 4)) { - throw new Error('Решатель не привел замок к целевому состоянию.'); - } - - const plateSwitches = commands.reduce((count, command, index) => { - if (index === 0 || command.plate === commands[index - 1].plate) { - return count; - } - return count + 1; - }, 0); - - return { - status: 'solved', - initialState, - targetState, - commands: commands.map((command) => ({ ...command })), - finalState, - metrics: { - commands: commands.length, - divisions: commands.reduce((sum, command) => sum + command.steps, 0), - plateSwitches, - }, - }; -} - -function formatConsoleResult(result) { - if (result.status === 'unsolvable') { - return 'Решение не найдено.'; - } - if (result.commands.length === 0) { - return 'Замок уже открыт.'; - } - - const directionNames = { left: 'влево', right: 'вправо' }; - const lines = result.commands.map((command, index) => ( - `${index + 1}. Пластина ${command.plate} - ${directionNames[command.direction]} x${command.steps}` - )); - return [`Найдено команд: ${result.commands.length}`, ...lines].join('\n'); -} - -return Object.freeze({ createSolveResult, formatConsoleResult }); -})(); - -// src/search-limits.mjs -modules["src/search-limits.mjs"] = (() => { -/** Resource failures are deliberately distinct from a proof of unreachability. */ -class SearchLimitError extends Error { - constructor(limit, maximum, used, options) { - super(`Превышен ресурсный предел поиска: ${limit} (предел ${maximum}, требуется ${used}).`, options); - this.name = 'SearchLimitError'; - this.limit = limit; - this.maximum = maximum; - this.used = used; - } -} - -const DEFAULTS = Object.freeze({ - maxVisited: 2_000_000, - maxExpanded: 1_000_000, - maxFrontier: 1_000_000, - maxDenseBytes: 64 * 1024 * 1024, -}); - -function normalizeSearchOptions(options = {}) { - if (options === null || typeof options !== 'object' || Array.isArray(options)) { - throw new TypeError('Параметры поиска должны быть объектом.'); - } - const algorithm = options.algorithm === undefined ? 'matrix-astar' : options.algorithm; - if (algorithm !== 'matrix-astar' && algorithm !== 'bfs') { - throw new TypeError('algorithm должен быть matrix-astar или bfs.'); - } - const result = { algorithm }; - for (const [name, fallback] of Object.entries(DEFAULTS)) { - const value = options[name] === undefined ? fallback : options[name]; - const minimum = name === 'maxVisited' || name === 'maxFrontier' ? 1 : 0; - if (!Number.isSafeInteger(value) || value < minimum) { - throw new TypeError(`${name} должен быть безопасным целым числом не меньше ${minimum}.`); - } - result[name] = value; - } - return result; -} - -class SearchBudget { - constructor(options) { - this.options = options; - this.visited = 0; - this.expanded = 0; - } - - check(name, used) { - if (used > this.options[name]) throw new SearchLimitError(name, this.options[name], used); - } - - visit() { - this.check('maxVisited', this.visited + 1); - this.visited += 1; - } - - expand() { - this.check('maxExpanded', this.expanded + 1); - this.expanded += 1; - } -} - -/** Validate encoding before matrix work, early returns, or dense allocation. */ -function prepareSearch({ state, links }) { - const n = state.length; - const size = 7 ** n; - if (!Number.isSafeInteger(size)) { - throw new SearchLimitError('stateEncoding', Number.MAX_SAFE_INTEGER, size); - } - const powers = Array.from({ length: n }, (_, index) => 7 ** (n - index - 1)); - let source = 0; - let goal = 0; - for (let index = 0; index < n; index += 1) { - source += (state[index] - 1) * powers[index]; - goal += 3 * powers[index]; - } - const effects = links.map((row, plate) => { - const targets = []; - const signs = []; - let offset = 0; - for (let target = 0; target < n; target += 1) { - const sign = plate === target ? 1 : row[target]; - if (sign === 0) continue; - targets.push(target); - signs.push(sign); - offset += sign * powers[target]; - } - return { targets, signs, offset }; - }); - return { n, size, source, goal, effects }; -} - -function decodeDigits(code, digits) { - for (let index = digits.length - 1; index >= 0; index -= 1) { - digits[index] = code % 7; - code = Math.floor(code / 7); - } -} - -function movementLimits(digits, effect) { - let plus = 6; - let minus = 6; - for (let index = 0; index < effect.targets.length; index += 1) { - const position = digits[effect.targets[index]]; - if (effect.signs[index] === 1) { - if (6 - position < plus) plus = 6 - position; - if (position < minus) minus = position; - } else { - if (position < plus) plus = position; - if (6 - position < minus) minus = 6 - position; - } - } - return [plus, minus]; -} - -function commandFor(plate, delta) { - return { plate: plate + 1, direction: delta > 0 ? 'left' : 'right', steps: Math.abs(delta) }; -} - -return Object.freeze({ SearchLimitError, normalizeSearchOptions, SearchBudget, prepareSearch, decodeDigits, movementLimits, commandFor }); -})(); - -// src/search-bfs.mjs -modules["src/search-bfs.mjs"] = (() => { -const { decodeDigits, movementLimits, SearchLimitError } = modules["src/search-limits.mjs"]; - -function unpack(packed) { - const plateDirection = Math.floor(packed / 6); - return { - plate: Math.floor(plateDirection / 2) + 1, - direction: plateDirection % 2 === 0 ? 'left' : 'right', - steps: packed % 6 + 1, - }; -} - -/** Exact macro BFS. Loop order is the original public tie-breaking contract. */ -function searchBfs(prepared, budget) { - const { n, size, source, goal, effects } = prepared; - budget.visit(); - if (source === goal) return []; - - // Signed parents need two sentinel values. Queue stores only the live frontier. - const capacity = Math.min(size, budget.options.maxVisited, budget.options.maxFrontier); - const denseBytes = size * 5 + capacity * 4; - const dense = size <= 0x7fffffff && denseBytes <= budget.options.maxDenseBytes; - let parents; - let actions; - let queue; - try { - parents = dense ? new Int32Array(size).fill(-2) : new Map(); - actions = dense ? new Uint8Array(size) : null; - queue = dense ? new Int32Array(capacity) : []; - } catch (error) { - if (!(error instanceof RangeError)) throw error; - throw new SearchLimitError('maxDenseBytes', budget.options.maxDenseBytes, denseBytes, { cause: error }); - } - if (dense) parents[source] = -1; - else parents.set(source, { parent: -1, action: -1 }); - queue[0] = source; - let head = 0; - let tail = 1 % capacity; - let frontier = 1; - const digits = new Uint8Array(n); - - function reconstruct(code) { - const commands = []; - while (code !== source) { - if (dense) { - commands.push(unpack(actions[code])); - code = parents[code]; - } else { - const node = parents.get(code); - commands.push(unpack(node.action)); - code = node.parent; - } - } - return commands.reverse(); - } - - while (frontier > 0) { - budget.expand(); - const code = queue[head]; - if (!dense) queue[head] = undefined; - head = (head + 1) % capacity; - frontier -= 1; - decodeDigits(code, digits); - for (let plate = 0; plate < n; plate += 1) { - const limits = movementLimits(digits, effects[plate]); - for (let direction = 0; direction < 2; direction += 1) { - const sign = direction === 0 ? 1 : -1; - for (let steps = limits[direction]; steps >= 1; steps -= 1) { - const next = code + sign * steps * effects[plate].offset; - if (dense ? parents[next] !== -2 : parents.has(next)) continue; - budget.visit(); - const action = (plate * 2 + direction) * 6 + steps - 1; - if (dense) { - parents[next] = code; - actions[next] = action; - } else parents.set(next, { parent: code, action }); - if (next === goal) return reconstruct(next); - budget.check('maxFrontier', frontier + 1); - queue[tail] = next; - tail = (tail + 1) % capacity; - frontier += 1; - } - } - } - } - return null; -} - -return Object.freeze({ searchBfs }); -})(); - -// src/matrix-analysis.mjs -modules["src/matrix-analysis.mjs"] = (() => { -const { SearchLimitError } = modules["src/search-limits.mjs"]; - -const abs = (value) => value < 0n ? -value : value; - -function fraction(n, d = 1n) { - if (n === 0n) return { n: 0n, d: 1n }; - if (d < 0n) { n = -n; d = -d; } - let a = abs(n); - let b = d; - while (b !== 0n) [a, b] = [b, a % b]; - return { n: n / a, d: d / a }; -} - -const divide = (a, b) => fraction(a.n * b.d, a.d * b.n); -const subtractProduct = (a, b, c) => fraction(a.n * b.d * c.d - b.n * c.n * a.d, a.d * b.d * c.d); - -/** - * Exact necessary equation A z = goal - state. links[source][target] is - * transposed into A[target][source]; selected-plate movement is the diagonal 1. - * Rational inconsistency or a fractional unique z is a proof of no integer path. - * Rank deficiency alone says nothing about whether a path exists. - */ -function analyzeMatrix({ state, links }) { - const n = state.length; - const rows = Array.from({ length: n }, (_, target) => [ - ...Array.from({ length: n }, (_, source) => fraction(BigInt(source === target ? 1 : links[source][target]))), - fraction(BigInt(4 - state[target])), - ]); - const columns = []; - let rank = 0; - for (let column = 0; column < n; column += 1) { - let pivot = rank; - while (pivot < n && rows[pivot][column].n === 0n) pivot += 1; - if (pivot === n) continue; - [rows[rank], rows[pivot]] = [rows[pivot], rows[rank]]; - const divisor = rows[rank][column]; - for (let j = column; j <= n; j += 1) rows[rank][j] = divide(rows[rank][j], divisor); - for (let row = 0; row < n; row += 1) { - if (row === rank || rows[row][column].n === 0n) continue; - const factor = rows[row][column]; - for (let j = column; j <= n; j += 1) rows[row][j] = subtractProduct(rows[row][j], factor, rows[rank][j]); - } - columns.push(column); - rank += 1; - } - for (let row = rank; row < n; row += 1) { - if (rows[row][n].n !== 0n) return { kind: 'inconsistent', rank }; - } - if (rank < n) return { kind: 'singular', rank }; - const exact = Array(n); - for (let row = 0; row < n; row += 1) exact[columns[row]] = rows[row][n]; - if (exact.some((value) => value.d !== 1n)) return { kind: 'noninteger', rank }; - const net = exact.map(({ n: numerator }) => { - if (abs(numerator) > BigInt(Number.MAX_SAFE_INTEGER)) { - throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, String(numerator)); - } - return Number(numerator); - }); - const lowerBound = net.reduce((sum, value) => sum + Math.ceil(Math.abs(value) / 6), 0); - if (!Number.isSafeInteger(lowerBound)) { - throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, lowerBound); - } - return { kind: 'unique-integer', rank, net, lowerBound }; -} - -return Object.freeze({ analyzeMatrix }); -})(); - -// src/matrix-search.mjs -modules["src/matrix-search.mjs"] = (() => { -const { analyzeMatrix } = modules["src/matrix-analysis.mjs"]; -const { searchBfs } = modules["src/search-bfs.mjs"]; -const { commandFor, decodeDigits, movementLimits, SearchLimitError } = modules["src/search-limits.mjs"]; - -/** One entry per open state; decrease-key never leaves obsolete heap nodes. */ -class IndexedHeap { - nodes = []; - - before(a, b) { - return a.g + a.h < b.g + b.h || - (a.g + a.h === b.g + b.h && (a.h < b.h || (a.h === b.h && a.sequence < b.sequence))); - } - - up(node) { - let index = node.heapIndex; - while (index > 0) { - const parent = Math.floor((index - 1) / 2); - if (!this.before(node, this.nodes[parent])) break; - this.nodes[index] = this.nodes[parent]; - this.nodes[index].heapIndex = index; - index = parent; - } - this.nodes[index] = node; - node.heapIndex = index; - } - - push(node) { - node.heapIndex = this.nodes.length; - this.nodes.push(node); - this.up(node); - } - - pop() { - const first = this.nodes[0]; - const last = this.nodes.pop(); - first.heapIndex = -1; - if (this.nodes.length === 0) return first; - let index = 0; - while (true) { - let child = index * 2 + 1; - if (child >= this.nodes.length) break; - if (child + 1 < this.nodes.length && this.before(this.nodes[child + 1], this.nodes[child])) child += 1; - if (!this.before(this.nodes[child], last)) break; - this.nodes[index] = this.nodes[child]; - this.nodes[index].heapIndex = index; - index = child; - } - this.nodes[index] = last; - last.heapIndex = index; - return first; - } -} - -/** - * A sufficient certificate, never an unreachability test. A legal single action - * for each nonzero net entry attains sum ceil(abs(net[i])/6), a global bound. - * An unsuccessful greedy attempt is discarded and full A* still runs. - */ -function greedyCertificate(prepared, net, budget) { - if (net.some((value) => Math.abs(value) > 6)) return null; - const remaining = [...net]; - const digits = new Uint8Array(prepared.n); - decodeDigits(prepared.source, digits); - const commands = []; - let pending = remaining.filter(Boolean).length; - while (pending > 0) { - let selected = -1; - for (let plate = 0; plate < prepared.n; plate += 1) { - const delta = remaining[plate]; - if (delta === 0) continue; - const limits = movementLimits(digits, prepared.effects[plate]); - if (Math.abs(delta) <= limits[delta > 0 ? 0 : 1]) { selected = plate; break; } - } - if (selected === -1) return null; - const delta = remaining[selected]; - const effect = prepared.effects[selected]; - for (let index = 0; index < effect.targets.length; index += 1) { - digits[effect.targets[index]] += delta * effect.signs[index]; - } - commands.push(commandFor(selected, delta)); - remaining[selected] = 0; - pending -= 1; - } - // Successful scheduling accounts for its path states and expansions too. - budget.check('maxVisited', commands.length + 1); - budget.check('maxExpanded', commands.length); - return commands; -} - -function reconstruct(node) { - const commands = []; - while (node.previous !== null) { - commands.push(commandFor(node.plate, node.delta)); - node = node.previous; - } - return commands.reverse(); -} - -/** - * Invertible A makes remaining net r unique for every physical state. An action - * changes one r[i] by at most six, hence h=sum ceil(abs(r[i])/6) is consistent. - * Closed nodes are final; the first popped goal has the minimum action count. - */ -function searchMatrix(definition, prepared, budget) { - const { n, source, goal, effects } = prepared; - if (source === goal) { budget.visit(); return []; } - const analysis = analyzeMatrix(definition); - if (analysis.kind === 'inconsistent' || analysis.kind === 'noninteger') return null; - if (analysis.kind === 'singular') return searchBfs(prepared, budget); - const certificate = greedyCertificate(prepared, analysis.net, budget); - if (certificate !== null) return certificate; - - budget.visit(); - const root = { - key: source, g: 0, h: analysis.lowerBound, r: analysis.net, - previous: null, sequence: 0, heapIndex: -1, closed: false, - }; - const heap = new IndexedHeap(); - heap.push(root); - const records = new Map([[source, root]]); - const digits = new Uint8Array(n); - let sequence = 1; - while (heap.nodes.length > 0) { - const node = heap.pop(); - if (node.key === goal) return reconstruct(node); - budget.expand(); - node.closed = true; - decodeDigits(node.key, digits); - for (let plate = 0; plate < n; plate += 1) { - const limits = movementLimits(digits, effects[plate]); - const preferred = node.r[plate] < 0 ? -1 : 1; - for (let side = 0; side < 2; side += 1) { - const sign = side === 0 ? preferred : -preferred; - const limit = limits[sign > 0 ? 0 : 1]; - for (let steps = limit; steps >= 1; steps -= 1) { - const delta = sign * steps; - const key = node.key + delta * effects[plate].offset; - const g = node.g + 1; - const existing = records.get(key); - if (existing && (existing.closed || existing.g <= g)) continue; - if (existing) { - // The physical state uniquely determines r and h, so only g and - // the predecessor change. Its predecessor is already finalized. - existing.g = g; - existing.previous = node; - existing.plate = plate; - existing.delta = delta; - heap.up(existing); - continue; - } - budget.visit(); - budget.check('maxFrontier', heap.nodes.length + 1); - const r = [...node.r]; - r[plate] -= delta; - const h = node.h - Math.ceil(Math.abs(node.r[plate]) / 6) + Math.ceil(Math.abs(r[plate]) / 6); - if (!Number.isSafeInteger(r[plate]) || !Number.isSafeInteger(g + h)) { - throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, String(r[plate])); - } - const next = { key, g, h, r, previous: node, plate, delta, sequence: sequence++, heapIndex: -1, closed: false }; - records.set(key, next); - heap.push(next); - } - } - } - } - return null; -} - -return Object.freeze({ searchMatrix }); -})(); - -// src/index.mjs -modules["src/index.mjs"] = (() => { -const { validateLockDefinition } = modules["src/lock-definition.mjs"]; -const { createSolveResult } = modules["src/result.mjs"]; -const { searchBfs } = modules["src/search-bfs.mjs"]; -const { searchMatrix } = modules["src/matrix-search.mjs"]; -const { normalizeSearchOptions, prepareSearch, SearchBudget } = modules["src/search-limits.mjs"]; - -function solveLock(value, options) { - const limits = normalizeSearchOptions(options); - const definition = validateLockDefinition(value); - const prepared = prepareSearch(definition); - const budget = new SearchBudget(limits); - const commands = limits.algorithm === 'bfs' - ? searchBfs(prepared, budget) - : searchMatrix(definition, prepared, budget); - return createSolveResult(definition, commands); -} - -const { applyCommand, generateTransitions } = modules["src/transition.mjs"]; -const { SearchLimitError } = modules["src/search-limits.mjs"]; - -return Object.freeze({ solveLock, applyCommand, generateTransitions, SearchLimitError }); -})(); - -globalThis.GothicLockSolver = modules["src/index.mjs"]; -})(); diff --git a/docs/algorithm.md b/docs/algorithm.md index 5bf9705..3fb8ec3 100644 --- a/docs/algorithm.md +++ b/docs/algorithm.md @@ -1,161 +1,156 @@ -# Модель, оптимальность и пределы вычисления +# Model, optimality and computational complexity -## Что минимизируется +## Objective and coordinates -Состояние замка — вектор `x` длины N, где каждое значение находится в `1..7`. -Матрица интерфейса визуализирует этот вектор; матрица зависимостей `L` имеет размер -N×N. Строка `L[i]` описывает влияние выбранной пластины i на остальные пластины. -Связи направленные, не распространяются каскадом, диагональ `L` равна нулю. +The state is a vector `x` of N integers in 1–7. The UI's grid visualizes this +vector; the dependency matrix `L` is N×N, with `L[source][target]` describing a +direct influence. Its diagonal is zero. Effects do not cascade. -Команда `(i, k)`, где `k ∈ {-6,…,-1,1,…,6}`, стоит одно **действие**. -Она изменяет положение выбранной пластины на k и применяет её прямые связи. -Каждое промежуточное положение обязано оставаться в `1..7`. -Недопустимое перемещение блокируется целиком. +A command `[i, k]`, where `i` is zero-based and `k` is one of −6…−1 or +1…+6, +costs one **action**. It changes the selected pin and its directly linked pins. +Every intermediate pin position must remain in 1–7. An illegal action is blocked +in its entirety. -В отчётах используются отдельные показатели: +| Metric | Definition | +| --- | --- | +| A | Number of commands, or grouped actions | +| U | Number of distinct selected plates, excluding indirectly affected plates | +| C | Number of unit shifts: sum of absolute command deltas | +| Switches | Changes of selected plate between adjacent commands | -| Обозначение | Значение | -|---|---| -| A | Количество команд: серий сдвигов выбранной пластины | -| U | Количество разных выбранных пластин; косвенно затронутые связи не считаются | -| C | Количество единичных сдвигов: сумма `steps` | -| plateSwitches | Число смен выбранной пластины между соседними командами | +A successfully returned solution minimizes A. Minimum U or C among equal-A +solutions is not a general guarantee. An optimal action sequence never has two +adjacent commands selecting the same plate: movements in the same direction can +be combined, and opposite movements can be cancelled. The combined displacement +still lies within the plate's six-position travel range. Thus a nonempty shortest +solution has A−1 switches, although it may revisit a plate later and have U` preserves the dimensions of a fixed-length state tuple. + +```ts +const state = [6, 2] as const satisfies State; +const links = [[0, -1], [0, 0]] as const satisfies Links; +const result = solveLock(state, links); +``` + +The state determines the matrix size. Literal tuples with too few plates, +wrong dimensions or out-of-range values fail type checking. Dynamically sized +`readonly Position[]` arrays are supported and checked at runtime. JSON remains +untrusted input regardless of a caller's TypeScript annotations. + +## Two inputs + +`solveLock(state, links)` takes no third options object. `state[i]` is the numeric +pin position on plate `i`. Both plate indices and matrix indices start at zero; +pin positions retain their game labels 1 through 7. All target positions are 4. + +The square matrix is **source first**: `links[i][j]` specifies the direct effect +on plate `j` when selecting plate `i`. A diagonal value other than zero is invalid. +The selected plate moves independently of the links. A reverse link has value −1, +a synchronous link +1, and no link 0. Effects apply once and never cascade. + +The solver validates dense arrays, dimensions, integers and ranges. It makes +immutable internal copies; caller-owned arrays remain unchanged, including on +failure. `readonly` expresses the type contract but does not replace validation. + +## Commands and outcomes + +`[i, k]` moves pin `i` by signed numeric displacement `k` and applies its links. +Zero displacement is not a command. A command costs one action irrespective of +`abs(k)`. Each affected pin must remain within 1–7 during every unit shift. + +The result is a sequence of these commands, `[]` if already open, or `null` if +unreachability has been proved. It is deterministic for identical valid inputs +within a version. Consumers must not depend on a particular tie-breaking path +across versions; minimum actions and legal completion are the guarantees. + +`LockInputError` signals malformed input. `SearchLimitError` exposes a documented +`limit` discriminator and `maximum`; it signals unavailable computation, never +unreachability. The current resource limits are documented with +[the algorithm](algorithm.md#computation-limits). + +```ts +try { + const result = solveLock([6, 2], [[0, -1], [0, 0]]); + if (result === null) { + console.log("No legal solution exists."); + } else { + for (const [index, delta] of result) console.log(index, delta); + } +} catch (error) { + if (error instanceof LockInputError) console.error("Invalid lock", error.message); + else if (error instanceof SearchLimitError) console.error("Search limit", error.limit); + else throw error; +} +``` + +Positive delta increases the numbered pin position. Physical left/right may be +opposite depending on whether an interface describes plate movement or pin +movement. That conversion belongs in the adapter, not in the matrix solver. + +## Web Workers + +For a local installed or copied ESM bundle, a module Worker can isolate synchronous +search from the page's main thread: + +```js +// solver-worker.mjs, hosted by the integrator +import { solveLock } from "https://cdn.jsdelivr.net/npm/gothic-lock-solver@VERSION/dist/gothic-lock-solver.min.mjs"; + +self.onmessage = ({ data: { state, links } }) => { + try { + self.postMessage({ commands: solveLock(state, links) }); + } catch (error) { + self.postMessage({ error: String(error) }); + } +}; +``` + +```js +// page.js +const worker = new Worker(new URL("./solver-worker.mjs", import.meta.url), { type: "module" }); +worker.onmessage = ({ data }) => console.log(data); +worker.postMessage({ state: [6, 2], links: [[0, -1], [0, 0]] }); +``` + +Host the Worker entry on your own origin and pin the library's exact CDN version. +Browser CSP must permit the chosen script source. No Worker wrapper is required +by the core contract; message shape and cancellation belong to the integrator. + +## CLI + +The fifth distribution file bundles the same solver and a Node.js file adapter. +It requires no sibling files or installed dependencies. + +```sh +node dist/gothic-lock-solver.cli.mjs examples/lock.example.json +node dist/gothic-lock-solver.cli.mjs examples/lock.example.json --output solution.json +# Equivalent installed npm bin: +gothic-lock-solver lock.json --output solution.json +# Repository source entry retained for development: +node solve-lock.mjs examples/lock.example.json +``` + +The input file is a JSON object with `state` and `links`. The console is Russian +for compatibility. The optional output preserves the original structured format: + +```json +{ + "status": "solved", + "initialState": [6, 2], + "targetState": [4, 4], + "commands": [{ "plate": 1, "direction": "right", "steps": 2 }], + "finalState": [4, 4], + "metrics": { "commands": 1, "divisions": 2, "plateSwitches": 0 } +} +``` + +Here plate numbers are one-based, `left` means positive numeric displacement, +and `right` means negative. A proved unreachable result has `status: "unsolvable"`, +`initialState`, `targetState`, and `commands: []`, without success metrics. +An open lock has `status: "solved"` and an empty command list. + +Exit codes: 0 solved, 2 proved unreachable, 1 validation, IO or resource error. +With `--output`, the destination is created or overwritten before a success +message is printed. Without it, no result file is created. + +A CLI process measures startup, parsing and filesystem work as well as the solver. +Use the paired [benchmark harness](../benchmarks/README.md) for algorithm timings; +use the CLI to measure your actual end-to-end command-line workload. diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..1b0131b --- /dev/null +++ b/docs/development.md @@ -0,0 +1,126 @@ +# Development and architecture + +## Toolchain + +Use pnpm 12.3.4 and Node.js 24 or 26 for development. Exact dependency versions +and integrity hashes are recorded in `package.json` and `pnpm-lock.yaml`. + +| Tool | Pinned version | Responsibility | +| --- | --- | --- | +| TypeScript | 7.0.2 | Strict type checking and declarations | +| Vite | 8.2.2 | All five runtime bundles | +| Vitest | 5.0.0 | Node/browser behavior tests, coverage and developer benchmarks | +| Playwright | 1.63.0 | Real Chromium, Firefox, WebKit consumers | +| Oxlint | 1.81.0 | Static checks; warnings fail | + +Production consumers use emitted JavaScript; they do not install this toolchain. +The supported consumer runtime range is Node.js 22, 24 and 26, declared in +`package.json`. Verification uses their latest maintained patch releases (currently +22.23.2, 24.19.0 and 26.8.1); use the latest patch of your chosen line. Browser testing uses real engines, not jsdom. + +```sh +pnpm install --frozen-lockfile +pnpm exec playwright install --with-deps chromium firefox webkit +pnpm test +pnpm test:coverage +``` + +`pnpm test` runs static checks, Node tests, Vite builds, distribution checks, +browser projects, unchanged-file E2E and clean package verification. Coverage is +also a required release gate. The benchmark is deliberately separate so it can +run without concurrent CPU-heavy test/build tasks: + +```sh +pnpm benchmark --reference ../unlockmyloot --output-dir artifacts/benchmark +``` + +See [the benchmark instructions](../benchmarks/README.md) for pinning upstream, +comparing a PR target, creating snapshots and interpreting noise. + +## Strictness and the dependency patch + +`tsconfig.json` enables the relevant strict flags, checked indexed access, exact +optional properties, explicit type imports, unused checks and library checking. +The build config emits declarations with the same source contract. No `any`, +non-null assertions or suppression comments are permitted in production code. +Negative type tests use `@ts-expect-error` to prove invalid imports/calls fail. +Runtime validation handles JSON, sparse arrays and values that TypeScript cannot +protect at an external boundary. + +Vitest 5.0.0 currently has three declaration incompatibilities under these checks: +its config declaration imports an absent `@vitest/expect` dependency, and the +resolved `provider`/`vmMemoryLimit` option types include explicit `undefined` +while their optional declarations omit it. The narrow checked-in +`patches/vitest@5.0.0.patch` corrects declarations only, using its direct Chai +dependency and matching those option unions. It does not change runtime code or +turn off `skipLibCheck` diagnostics. The official npm tarball was independently +verified against registry integrity before the patch was applied. + +Review this patch on every Vitest update and remove it when upstream declarations +support these checks. Do not silently downgrade the current tools or force an +incompatible peer dependency. The patch is maintained through pnpm's documented +patched-dependency mechanism. + +## Domain boundaries + +`LockModel` owns input invariants and copies. `PreparedSearch` owns encoded effects +and safe state representation. `SearchBudget` and `IndexedHeap` encapsulate state +with operational invariants. Matrix elimination uses pure exact-arithmetic +functions; strategies implement one small internal search contract. The facade +chooses the strategy without exposing the search machinery. + +The core has no filesystem, DOM, UI or network dependency. CLI formatting and IO +live in `cli/`. Benchmarks own their independent replay and reference loading; +production code does not depend on benchmark fixtures. The release scripts +orchestrate already verified files and do not implement a second bundler. + +This applies DDD through explicit domain terms and invariant ownership, BDD +through observable input/outcome scenarios, and TDD through a failing behavioral +check before its implementation. It does not require a DDD framework, Gherkin, +DI container or class for every function. Composition and plain functions keep +the implementation proportionate to a small mathematical library. + +## Tests and evidence + +- Unit and oracle tests protect mechanics, optimal action counts, validation, + resource outcomes and nonmutation. +- The same catalog is replayed on Node and real browser projects. +- Static consumer tests verify named/namespace imports and reject a default export. +- Distribution E2E tests serve unchanged bundle bytes from a static HTTP server, + exercise every format and all 45 inputs, test Workers and classic `file://`, + and detect unexpected runtime requests. +- Clean tarball verification checks package contents, type resolution, imports, + browser use and the installed CLI. A failed required consumer prevents a + package from being marked verified. +- Coverage includes all shipped core and CLI sources, including unimported files; + statements, branches, functions and lines each require at least 80%. + +Each gate has a purpose. Do not add tests that merely mirror private methods, +or change snapshots and thresholds to conceal regressions. Timing, peak RSS, +quality and environment compatibility are separate evidence. A benchmark with +unresolved noise remains inconclusive. + +## Contribution sequence + +1. Read the active specification and tests, then add the failing behavioral case. +2. Implement the smallest change that preserves domain invariants. +3. Run focused tests and static checks; then run the full local production checks. +4. Measure candidate and baseline together without competing benchmark processes. +5. Have an independent reviewer check claims, tests, code and owner principles. +6. Only after local acceptance, put the working commands in official GitHub Actions. + +The migration is one draft PR based on the preserved reference branch. Publishing +and merging are separate future actions. Historical design documents and reports +remain for provenance; current API, algorithm and development docs are linked +from README and AGENTS instead of duplicated into another wiki. + +## Official references + +- [TypeScript handbook](https://www.typescriptlang.org/docs/handbook/intro.html) +- [TypeScript strict configuration](https://www.typescriptlang.org/tsconfig/strict.html) +- [Vite TypeScript behavior](https://vite.dev/guide/features#typescript) +- [Vite build options](https://vite.dev/config/build-options) +- [Vitest Browser Mode](https://vitest.dev/guide/browser/) +- [Vitest coverage](https://vitest.dev/guide/coverage.html) +- [Playwright browsers](https://playwright.dev/docs/browsers) +- [pnpm patched dependencies](https://pnpm.io/settings#patcheddependencies) diff --git a/docs/releasing.md b/docs/releasing.md new file mode 100644 index 0000000..19d4a79 --- /dev/null +++ b/docs/releasing.md @@ -0,0 +1,132 @@ +# Packaging and releases + +The distribution is one public npm package, automatically served file-by-file by +jsDelivr, followed by a GitHub Release with the exact verified bytes and evidence. +No CDN account, SDK, Action, upload or separate deployment is involved. + +## Before the first publication + +This branch does not publish anything. The owner must establish the npm package +and configure its GitHub trusted publisher before the first automatic release. +The package name was available during planning; that check does not reserve it. +GitHub and npm account names need not match. + +Configure npm trusted publishing for the owner repository, `release.yml` workflow +and the release environment used by that workflow. Permit direct publication +in the publisher settings where npm distinguishes staged from direct publishing. +Use the [official npm instructions](https://docs.npmjs.com/trusted-publishers/). +Do not put a token in chat or repository files. Once setup is complete, set the +repository readiness variable `NPM_PUBLICATION_READY=true`. + +The automated release uses official npm CLI 12.0.2 with OIDC and provenance on a +GitHub-hosted runner. GitHub assets are managed by official GitHub CLI 2.100.0. +All referenced Actions are from their tool/service authors and pinned to verified +full commit SHAs. pnpm owns dependency installation, Vite owns bundling, and +Vitest/Playwright own the checks. Workflow YAML only connects working local steps. + +## Local acceptance precedes workflow integration + +Run the full local test command, coverage, clean installed package verification +and the all-45 paired benchmark before creating or updating the workflow. +Keep exact failure evidence and fix the cause locally. Required browser projects +cannot be replaced by Node VM tests or skipped because a runner is inconvenient. + +`pnpm build` produces five self-contained runtime files and declarations under +`dist/`, plus a build manifest binding version, source SHA and file hashes. +`pnpm verify:package` packs those existing bytes, installs the archive into an +isolated consumer, checks imports/types/CLI and reruns browser consumers against +the installed files. Only after all required checks pass does it write +`artifacts/package/package.tgz` and its checksum. It does not publish. + +A completed benchmark run supplies `benchmark.json`, `benchmark.md` and +`benchmark-evidence.json`. Prepare a release locally from the exact verified files: + +```sh +node scripts/release.ts prepare --benchmark-dir artifacts/benchmark/run-EXACT +node scripts/release.ts check +``` + +The default output is `artifacts/release`. Preparation never rebuilds or repacks. +A version/source mismatch, changed tarball file, incorrect report hash or failed +quality gate stops it. Benchmark evidence must identify the exact candidate +module and source commit. Prepare output is created exclusively to prevent +accidental replacement of an earlier release candidate. + +Both calibrated timing and whole-catalog peak-RSS comparisons must pass. An +inconclusive or regressed performance result requires explicit review of that +exact evidence, recorded by its SHA256 and a reason. This exception does not +waive malformed paths, lost solvability, worse deterministic quality or mismatched +source bytes. Ordinary code changes cannot silently accept their own regression. + +## Stable publication sequence + +After a future approved merge to main, the workflow validates the exact source +commit and prepares one immutable candidate: + +1. Run the existing local checks and matched baseline comparison; preserve reports. +2. Verify and retain one tarball containing the five runtime files, declarations, + package metadata, README and license. No consumer installation script builds it. +3. Publish that exact tarball to public npm with the agreed SemVer and `latest` tag. +4. Download the exact published version. Check registry integrity, tarball/file + identity, installation and module behavior without consumer credentials. +5. Verify all four core file URLs on jsDelivr for status, JavaScript MIME, CORS + and SHA256. Load them in real Chromium, Firefox and WebKit from another origin + and solve all 45 fixtures. The fifth file is a Node tool, not a browser script. +6. Create a GitHub draft release, attach the same runtime files, package archive, + checksum manifest and full evidence, verify assets, then finalize the release. + +Release notes contain the npm version, source SHA, browser snippets and the actual +quality/timing/memory verdicts compared with the previous stable release. The +first release explicitly uses the preserved reference rather than inventing a +previous stable release. PR comparisons obtain their baseline from the actual +PR target checkout; candidate snapshots are not accepted as their own baseline. + +## CDN contract + +After npm publication, the browser endpoint is: + +```text +https://cdn.jsdelivr.net/npm/gothic-lock-solver@VERSION/dist/gothic-lock-solver.min.js +``` + +Use the full version and one of the four already-built core filenames. Do not +use `latest`, version ranges, `+esm`, combine or generated minification endpoints +for reproducible integrations. Updating or rolling back means explicitly changing +the version. The npm archive already contains the exact served files. + +npm's documented delivery contract provides registry metadata and a package +tarball, not a supported browser URL for each unpacked JavaScript file. +GitHub Packages' npm registry requires authentication even for public package +installation. jsDelivr documents automatic delivery of public npm files without +an account and supplies the required anonymous browser endpoint. + +The budget is zero: public npm, jsDelivr and standard free public GitHub runners. +No paid larger runners, increased quotas, subscriptions or automatic paid overflow +are configured. Temporary CI artifacts have bounded retention; releases retain +the durable distribution and evidence. + +## Partial publication and canaries + +npm publication and GitHub Releases are not one atomic transaction. If npm +succeeds and later delivery verification fails, preserve the prepared artifact +and rerun the failed steps. An existing version is downloaded and checked for +identity; it is never overwritten or automatically unpublished. Changed bytes +require a new version. A temporary delivery failure allows a bounded verification +retry, not rebuilding under the same version. + +Regular PRs only produce validation artifacts. An optional manual canary uses a +unique prerelease version containing the source SHA and run identifiers, and the +separate npm `canary` tag. It does not move `latest`, replace the stable benchmark +baseline or become GitHub's latest stable release. Privileged publication logic +must always come from main, even when the candidate is built from an owner branch. +No fork PR receives publication credentials or a privileged workflow context. + +## Official references + +- [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) +- [npm publish](https://docs.npmjs.com/cli/v11/commands/npm-publish/) +- [GitHub Packages npm authentication](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#authenticating-to-github-packages) +- [jsDelivr npm delivery](https://github.com/jsdelivr/jsdelivr#npm) +- [GitHub CLI release creation](https://cli.github.com/manual/gh_release_create) +- [GitHub Actions security](https://docs.github.com/en/actions/reference/security/secure-use) +- [GitHub Actions billing and free public runners](https://docs.github.com/en/billing/concepts/product-billing/github-actions) diff --git a/docs/superpowers/plans/2026-09-07-typescript-library.md b/docs/superpowers/plans/2026-09-07-typescript-library.md new file mode 100644 index 0000000..b645aa6 --- /dev/null +++ b/docs/superpowers/plans/2026-09-07-typescript-library.md @@ -0,0 +1,108 @@ +# Gothic Lock Solver TypeScript Library Implementation Plan + +> **For agentic workers:** use superpowers:subagent-driven-development; follow this plan task by task with independent review. + +**Goal:** Выпустить проверяемую TypeScript-библиотеку с четырьмя самодостаточными форматами, доказательными сравнениями и подготовленным npm/Release workflow. + +**Architecture:** Один именованный facade solveLock делегирует проверенной модели и точным стратегиям поиска. Инструменты сборки, независимый oracle и измерения находятся вне runtime. npm и Release получают байты одного проверенного tarball; jsDelivr раздаёт файлы npm. + +**Tech Stack:** TypeScript 7.0.2, Vite 8.2.2, Vitest 5.0.0, Playwright 1.63.0, Oxlint 1.81.0, pnpm 12.3.4; актуальность перепроверена через npm registry. + +**Spec:** `docs/superpowers/specs/2026-09-07-typescript-library-design.md`. + +## Global Constraints + +- Только наш репозиторий, ветка feat/typescript-core-library; main и референс не меняются. +- Публичный API: `solveLock(state, links): readonly Command[] | null`, без default export и параметров настройки. +- Position=1..7, N>=2, goal=4; links[source][target]=-1/0/1, диагональ 0. Command=[zero-based index, signed numeric pin delta], delta!=0. +- Минимум действий A обязателен; U и C измеряются отдельно. Null означает доказанную недостижимость, не исчерпание ресурсов. +- Strict TypeScript, noUncheckedIndexedAccess, exactOptionalPropertyTypes и остальные флаги спецификации; без any, ts-ignore и необоснованных assertions. +- Ровно четыре независимых runtime-файла: gothic-lock-solver.mjs, gothic-lock-solver.min.mjs, gothic-lock-solver.js, gothic-lock-solver.min.js. IIFE global GothicLockSolver, ноль runtime-зависимостей. +- Node 24/26 и настоящие Chromium/Firefox/WebKit; обязательные среды не заменяются VM/jsdom. +- 45 исходных fixtures сохраняют hashes/provenance. Сторонний код AGPL не включается в пакет и репозиторий; подключается из закреплённого checkout. +- Метрики и baseline не перезаписываются автоматически; время/память с неопределённостью, без выдуманной точности. +- Только официальные Actions latest stable с SHA, официальный npm CLI/OIDC и gh; публичный npm и бесплатный CDN. + +## Ownership and interfaces + +| Task | Owns | Consumes / produces | +|---|---|---| +| 1 build and environment | package.json, lockfile, tsconfig*, vite/vitest/playwright config, .oxlintrc*, scripts/build*, scripts/verify-package*, tests/distribution*, tests/e2e*, tests/browser* | Consumes src/index.ts; produces dist four files plus declarations, package.tgz and `pnpm check`, `pnpm test:node`, `pnpm test:browser`, `pnpm test:e2e`, `pnpm build`, `pnpm verify:package` | +| 2 mathematical core | src/**/*.ts, tests/unit/core*, tests/helpers/oracle*, tests/types* | Produces named solveLock/types/errors; tests do not require bundle build | +| 3 evidence | benchmarks/**/*.ts, benchmarks/snapshots/*, tests/unit/benchmark*, scripts/benchmark*, benchmarks/README.md | Consumes public solveLock and pinned reference; produces `pnpm benchmark --reference PATH --output-dir PATH`, `pnpm benchmark:compare --baseline-module PATH --baseline-snapshot PATH --reference PATH --output-dir PATH` | +| 4 integration and delivery | .github/**, README.md, AGENTS.md, cli/**, solve-lock.mjs, docs/**, obsolete-file cleanup | Consumes all above commands; preserves repo CLI, prepares release/canary, produces draft PR and measured evidence | + +Task 1 and Task 2 share an interface, not source files; installing tooling may precede core tests. Task 3 starts on preserved fixtures and reference while Tasks 1/2 proceed. Main coordinator owns integration and removes obsolete production code only after equivalence checks. + +## Task 1: Build and verified consumers + +- [ ] Configure exact devDependencies and strict compiler options; install with pnpm and save lockfile. No optional lint/typecheck bypass. +- [ ] Write distribution tests against actual outputs: import both .mjs in Node; execute both classic files in browsers; exercise Worker and isolated HTML from a static server. +- [ ] Observe missing-artifact failure before implementing build. Configure Vite library ESM/IIFE twice for readable/minified outputs; verify actual minification and no imports/chunks/runtime globals requiring Node. +- [ ] Type declarations accompany package exports; no default export. Test negative type imports and clean installed tarball outside repo resolution. +- [ ] Vitest projects run shared core/catalog cases in Node and Chromium/Firefox/WebKit. Playwright E2E imports static bytes without Vite transformations. +- [ ] Complete build/package tests and report exact commands, engines and limitations. + +Expected consumer behavior: + +```ts +import { solveLock } from 'gothic-lock-solver'; +import * as GothicLockSolver from 'gothic-lock-solver'; +expect(solveLock([6, 2], [[0, -1], [0, 0]])).toEqual([[0, -2]]); +expect(GothicLockSolver.solveLock([4, 4], [[0, 0], [0, 0]])).toEqual([]); +``` + +## Task 2: Exact typed core + +- [ ] Write new tuple-contract behavior tests before the new entry exists. Test asymmetric direction/sign, no cascade, immutability, open/unsolvable, invalid sparse input and independent action optimality. +- [ ] Port the already proven matrix algorithm into typed single-responsibility model, exact rational functions, indexed heap, budget, A* and BFS. Preserve command order and resource limits; the old object API remains only in pinned reference code. +- [ ] Enforce compile-time tuple dimension checks where known, plus dynamic runtime validation. Document exactly which assertions are logically justified; no unverified non-null indexing. +- [ ] Differential-test all 441 N2 cases, seeded N3, singular/deadlocked/fractional matrices, N8 and safe wider state codes, limits separately from null. +- [ ] Run all 45 catalog cases with independent replay and expected minimum A. Apply each intermediate unit shift in oracle replay. +- [ ] Run typecheck and focused tests; provide self-review of mathematical invariants and time/memory impact. + +Representative red tests: + +```ts +expect(solveLock([6, 2], [[0, -1], [0, 0]])).toEqual([[0, -2]]); +expect(solveLock([1, 1, 1], [[0,-1,-1],[-1,0,-1],[-1,-1,0]])).toBeNull(); +expect(() => Reflect.apply(solveLock, undefined, [[0, 4], [[0,0],[0,0]]])).toThrow(); +``` + +## Task 3: Reproducible benchmark evidence + +- [ ] Preserve fixture hashes and isolate immutable reference matrix source from commit 6b1cfbc13bcec68f609d45d5dc97504dd84f1760 (or byte-identical current reference). Keep original Apache BFS and upstream pin. +- [ ] Write tests for independent tuple replay, metrics A/U/C/switches, compare regression/inconclusive, source tampering and baseline non-overwrite before implementing new harness. +- [ ] Produce stable quality snapshot for all 45 locks and compare against target branch snapshot. Store optimum A and candidate result metrics separately from historic upstream counts. +- [ ] Measure warmups/repetitions, median/p95/raw samples; alternate candidate/base in one environment. Calibrate unchanged-vs-itself before a performance verdict. Paired comparison must not rely on historical times from another machine. +- [ ] Measure process peak RSS in isolated workers separately from timing; record baseline/runtime cost and browser-memory unavailable explicitly. +- [ ] Report environment/source/fixture hashes, compared SHAs, bundle bytes, per-lock differences and full JSON/Markdown. Upstream adapter verifies pinned file/block hashes before executing external code. +- [ ] Add meaningful Vitest bench entry for developer runs; release verdict uses controlled comparison rather than a noisy single benchmark threshold. +- [ ] Run reference + candidate on all 45; report improvements and regressions honestly, fix algorithmic regressions rather than relaxing snapshot. + +Comparison test semantics: + +```ts +// A regression is always a deterministic failure; timing uncertainty cannot hide it. +expect(compareMetrics({ A: 4, U: 2, C: 8 }, { A: 3, U: 2, C: 8 }).quality).toBe('regression'); +// Historical timings alone never prove a matched-environment performance pass. +``` + +## Task 4: Integrate, document and deliver + +- [ ] Adapt repo CLI to invoke typed facade, preserving Russian terminal messages and explicit output file behavior; test CLI against actual built core. +- [ ] Prepare official pinned GitHub Actions CI: frozen install, lint/typecheck, Node/browser/package/E2E, pinned upstream checkout and base-branch comparison; upload visible evidence. +- [ ] Prepare main stable and optional manually invoked canary workflow: same checked tarball -> npm OIDC -> registry/CDN verification -> GitHub Release. Fail distinctly on partial publish; never overwrite npm versions or source evidence. +- [ ] Document package/bootstrap ownership, exports and all examples, exact mathematical guarantee and complexity, resource errors, benchmark interpretation, source/style conventions and reviewer commands. +- [ ] Remove superseded handwritten bundler and duplicate active .mjs core only after replacement passes; preserve original historical docs and pinned reference implementations. +- [ ] Run full `pnpm check`, package consumer tests, full benchmark compare, and independent whole-branch review. Fix material findings and recheck affected behavior. +- [ ] Publish source branch and generated evidence, create draft PR against feat/matrix-astar-benchmarks. Do not merge main, publish npm, create release, or modify upstream repository. + +## Verification record + +Before migration: `pnpm test` passes 113/113 tests, no failures/skips; Node 24.19.0. +Latest registry versions were checked before tooling installation. Main remains a87e739; reference branch remains 3cbbeed. + +## Accepted execution refinements (2026-09-07) + +The owner added a fifth Vite-built self-contained Node CLI (`gothic-lock-solver.cli.mjs`) with npm bin, maintained Node22 consumer support, strict >=80% statements/branches/functions/lines across src+cli, and English README/focused engineering docs. The four library formats and two-argument tuple API remain as specified. An independent reviewer reads the owner's public-safe principles and current official TypeScript7 documentation, reviews code and real behavior evidence. Workflows are the final step after all local checks and benchmarks; no .github implementation starts before local acceptance. All changes belong to one draft PR from the preserved reference branch. diff --git a/docs/superpowers/specs/2026-09-07-typescript-library-design.md b/docs/superpowers/specs/2026-09-07-typescript-library-design.md index e3ee9fa..2874eb8 100644 --- a/docs/superpowers/specs/2026-09-07-typescript-library-design.md +++ b/docs/superpowers/specs/2026-09-07-typescript-library-design.md @@ -1,6 +1,6 @@ # TypeScript-библиотека Gothic Lock Solver -Статус: **черновик для согласования, реализация не начата**. +Статус: **согласовано пользователем; реализация начата 2026-09-07**. Дата: 2026-09-07. Текущая задача ограничена нашим репозиторием. Название, выбранное пользователем: **Gothic Lock Solver**. @@ -42,6 +42,12 @@ function solveLock( ): readonly Command[] | null; ``` +ESM предоставляет именованный экспорт `solveLock` и экспорты типов; default export +не добавляется. Поддерживаются `import { solveLock } from 'gothic-lock-solver'` +и `import * as GothicLockSolver from 'gothic-lock-solver'`. Последний даёт вызов +`GothicLockSolver.solveLock(state, links)`, как classic global, без отдельного класса +или объекта-обёртки в публичном API. Пользователь подтвердил этот выбор. + Ровно два аргумента. N выводится из `state.length`; отдельные N, цель, алгоритм, бюджеты, callbacks и настройки в публичный вызов не добавляются. Внешнее API не возвращает телеметрию или оболочку с копиями входа. Для литерального tuple N @@ -184,7 +190,8 @@ baseline для сокрытия регрессии. Никаких обходо TypeScript `>=4.8.4 <6.1.0`; это несовместимо с требованием TypeScript 7 latest. Сборка Vite не заменяет проверку типов: отдельный typecheck обязателен. Среда разработки — актуальный Node.js LTS, поддерживаемый всем инструментарием; -начальная CI-матрица runtime — Node.js 24 и 26 с закреплением версий. +сборка и разработка — Node.js 24/26; consumer-матрица дополнена Node.js 22, +проверяется на последних maintained patch-версиях 22/24/26 с закреплением. ### GitHub Actions: только официальные актуальные подключения @@ -227,7 +234,7 @@ SHA/версии проходят обычные обязательные про реализации и закрепляется. Устаревшие официальные Actions и сторонние обёртки не добавляются ради формального использования `uses:` вместо команды. -## Четыре распространяемых файла +## Пять распространяемых файлов | Файл | Подключение | |---|---| @@ -235,8 +242,9 @@ SHA/версии проходят обычные обязательные про | `gothic-lock-solver.min.mjs` | Минифицированный ESM | | `gothic-lock-solver.js` | Читаемый classic script, глобальный `GothicLockSolver.solveLock` | | `gothic-lock-solver.min.js` | Минифицированный classic script | +| `gothic-lock-solver.cli.mjs` | Самодостаточный Node.js CLI, `node ` или npm bin `gothic-lock-solver` | -Каждый файл самодостаточен: без внешних imports, дополнительных chunks, обращения +Каждый из четырёх файлов ядра самодостаточен: без внешних imports, дополнительных chunks, обращения к сети, Node API и UI. Сборка использует один TypeScript entry point, форматы ESM и IIFE. Минификация не меняет контракт, ошибки и найденный результат. Текущий ручной ESM-assembler заменяется стандартной сборкой Vite. @@ -247,7 +255,14 @@ Vite library mode сама по себе может сохранять проб Декларации типов для ESM-путей, описание глобального API, лицензия и manifest с версией, commit SHA и хэшами сопровождают выпуск. Это вспомогательные файлы; -для исполнения потребитель выбирает ровно один из четырёх runtime-файлов. +для исполнения потребитель выбирает один подходящий файл. Пятый CLI-файл также +собирается Vite и включает ядро; разрешены только встроенные Node.js-модули для +файлов и потоков. Отдельная установка зависимостей или сборка ему не нужна. +Сохраняются текущие JSON-вход, `--output`, коды завершения 0/1/2 и русское +консольное представление. CLI явно документирует прежние физические направления +и нумерацию пластин с единицы; это адаптер числового контракта ядра. +Минимальная поддержанная версия Node.js фиксируется по проверенной матрице; +пакет проверяется также установкой tarball и запуском npm bin вне репозитория. ## Проверки реального использования @@ -275,7 +290,9 @@ Agent Browser рассмотрен отдельно: это CLI управлен 6. Неподдерживаемая среда или невозможность запустить браузер явно отражаются в результате. Пропущенная обязательная среда не даёт статуса полной готовности. -Покрытие публикуется как диагностический отчёт. Число тестов или процент покрытия +Покрытие statements, branches, functions и lines не ниже 80% по каждой метрике +для производственных исходников ядра и CLI — обязательный локальный и CI gate. +Отчёт включает также не импортированные тестами производственные файлы; проценты не подменяют независимую проверку механики и производственного импорта. ## Бенчмарк и snapshot @@ -323,6 +340,10 @@ Agent Browser рассмотрен отдельно: это CLI управлен ## CI, npm, GitHub Releases и доставка в браузер +Workflow реализуется последним: сначала локально проходят lint, typecheck, +unit/integration, coverage, сборка всех пяти файлов, чистый consumer, браузеры, +CLI и полное сравнение 45 замков. Независимое ревью проверяет код и доказательства. +Только затем эти уже работающие команды объединяются в GitHub workflow. PR запускает lint, typecheck, unit/integration, сборку, тесты dist/E2E и сравнение бенчмарка. JSON/Markdown, покрытие и четыре файла доступны как CI artifacts. Документируется одна команда локальной полной проверки и отдельные команды этапов. @@ -338,7 +359,7 @@ PR не публикует боевой `latest` и не получает пра GitHub хранит исходники, CI и релизы с готовыми файлами и доказательным отчётом. Пользователь готов рассмотреть jsDelivr после документального обоснования, почему сами npmjs.org и GitHub Packages не закрывают требуемый browser endpoint. -Выбор jsDelivr ниже остаётся предложением для согласования, а CDN — обязательным. +Для реализации выбран предложенный jsDelivr поверх npm; CDN обязателен. Документальные основания: @@ -414,7 +435,7 @@ npm-аккаунт владельца. Совпадение GitHub username с n GitHub latest release; если для неё создаётся GitHub Release, он помечается prerelease. Публичная публикация не запускается автоматически для каждого PR. -Пятый evidence-файл не является зависимостью ядра и не загружается потребителем +Дополнительный evidence-файл не является зависимостью ядра и не загружается потребителем при solveLock. У него версионированная JSON-схема: версия выпуска/source SHA, SHA предыдущего сравниваемого выпуска, fixture/source hashes, версии сред, параметры измерения, итоги A/U/C и времени/памяти, дельты, quality/performance @@ -478,12 +499,26 @@ CDN ответил 200, `Content-Type: application/javascript; charset=utf-8`, ## Документация и приёмка -README содержит назначение, алгоритмические гарантии, оба входа и все исходы, +README и актуальная документация пишутся на английском. README содержит назначение, алгоритмические гарантии, оба входа и все исходы, координатные конвенции, четыре способа подключения, Worker, поддержанные среды, команды проверки, чтение performance verdict, изменение snapshot и выпуск. AGENTS.md описывает реальные модули, инварианты, команды и границы изменений. Для работы с публичным проектом доступ к частной wiki не требуется. +Независимый reviewer сверяет финальное решение с public_safe принципами wiki: +TDD, DDD, BDD, SOLID, KISS, YAGNI и DRY. TDD подтверждается наблюдаемым red/green; +DDD выражается точной моделью и единым владельцем инвариантов, BDD — проверяемым +поведением потребителя. Не вводятся фреймворки, слои или классы ради названий. +TypeScript 7 и JavaScript idioms проверяются по официальной актуальной документации. +README ссылается на компактные документы о контракте, алгоритме, сложности, +разработке, benchmark evidence и публикации; AGENTS.md направляет к тем же +источникам, не создаёт дублирующую локальную wiki. Все изменения входят в один +новый draft PR от сохранённой референсной ветки. + +Во всех этапах npm/GitHub Release ниже комплект включает четыре файла ядра и +пятый CLI. CDN/browser-проверка относится к четырём файлам ядра; CLI проверяется +в Node.js из того же tarball. Evidence остаётся отдельным asset, не runtime-файлом. + Готовность подтверждается проходящим CI нового PR, проверкой всех 45 входов, принятыми performance-результатами, тестами всех публикуемых форматов и понятным планом первого выпуска. До согласованного merge стабильная публикация npm/Release diff --git a/package.json b/package.json index d1faffc..8e9238e 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,74 @@ { "name": "gothic-lock-solver", - "version": "0.1.0", - "private": true, + "version": "1.0.0", + "private": false, "type": "module", "engines": { - "node": ">=20" + "node": "^22.0.0 || ^24.0.0 || ^26.0.0" }, "scripts": { - "test": "node --test", - "solve": "node solve-lock.mjs", - "benchmark": "node scripts/benchmark.mjs", - "build:browser": "node scripts/build-browser.mjs", - "verify:browser-bundle": "node scripts/build-browser.mjs --check", - "verify:web-core": "node --experimental-vm-modules scripts/verify-web-core.mjs" + "lint": "oxlint --deny-warnings src cli scripts tests benchmarks *.ts", + "typecheck": "tsc --noEmit", + "check": "pnpm lint && pnpm typecheck", + "test": "pnpm check && pnpm test:coverage && pnpm build && pnpm test:distribution && pnpm test:browser && pnpm test:e2e && pnpm verify:package", + "test:node": "vitest run --project node", + "test:distribution": "vitest run --project distribution", + "test:browser": "vitest run --project chromium --project firefox --project webkit", + "test:e2e": "playwright test", + "test:coverage": "vitest run --project node --coverage", + "build": "node scripts/build.ts", + "verify:package": "node scripts/verify-package.ts", + "benchmark": "node scripts/benchmark.ts", + "benchmark:compare": "node scripts/benchmark-compare.ts", + "benchmark:vitest": "vitest bench --run --project \"node (bench)\"", + "solve": "node cli/main.ts" + }, + "description": "Exact shortest-action solver for Gothic 1 Remake plate locks.", + "license": "Apache-2.0", + "packageManager": "pnpm@12.3.4", + "repository": { + "type": "git", + "url": "git+https://github.com/Marcus-Rise/gothic-lock-solver.git" + }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "types": "./dist/index.d.ts", + "module": "./dist/gothic-lock-solver.mjs", + "unpkg": "./dist/gothic-lock-solver.min.js", + "jsdelivr": "./dist/gothic-lock-solver.min.js", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/gothic-lock-solver.mjs" + }, + "./dist/gothic-lock-solver.mjs": { + "types": "./dist/index.d.ts", + "import": "./dist/gothic-lock-solver.mjs" + }, + "./dist/gothic-lock-solver.min.mjs": { + "types": "./dist/index.d.ts", + "import": "./dist/gothic-lock-solver.min.mjs" + }, + "./dist/gothic-lock-solver.js": "./dist/gothic-lock-solver.js", + "./dist/gothic-lock-solver.min.js": "./dist/gothic-lock-solver.min.js", + "./package.json": "./package.json" + }, + "devDependencies": { + "@playwright/test": "1.63.0", + "@types/node": "26.4.1", + "@vitest/browser-playwright": "5.0.0", + "@vitest/coverage-v8": "5.0.0", + "oxlint": "1.81.0", + "playwright": "1.63.0", + "typescript": "7.0.2", + "vite": "8.2.2", + "vitest": "5.0.0" }, "bin": { - "gothic-lock-solver": "./solve-lock.mjs" + "gothic-lock-solver": "./dist/gothic-lock-solver.cli.mjs" } } diff --git a/patches/vitest@5.0.0.patch b/patches/vitest@5.0.0.patch new file mode 100644 index 0000000..93fd90b --- /dev/null +++ b/patches/vitest@5.0.0.patch @@ -0,0 +1,32 @@ +diff --git a/config.d.ts b/config.d.ts +index 36afa874abb35e02a7bb36d1ae0858e2a8b66b96..cd1f8bd910b018645c4bdc67f4a61a9f27268ec6 100644 +--- a/config.d.ts ++++ b/config.d.ts +@@ -1,3 +1,3 @@ +-// ensure `@vitest/expect` provides `chai` types +-import type {} from '@vitest/expect' ++// Vitest 5 bundles its expect declarations; chai remains a direct dependency. ++import type {} from 'chai' + export * from './dist/config.js' +diff --git a/dist/chunks/plugin.d.BbcoZhuj.d.ts b/dist/chunks/plugin.d.BbcoZhuj.d.ts +index f399a7658b43c34df38c976e6f41744d9af7a5fd..811802c8a90a5910edd9d708f70ef78bc8c5cd09 100644 +--- a/dist/chunks/plugin.d.BbcoZhuj.d.ts ++++ b/dist/chunks/plugin.d.BbcoZhuj.d.ts +@@ -3318,7 +3318,7 @@ interface BenchmarkUserOptions { + * + * @experimental + */ +- provider?: string; ++ provider?: string | undefined; + /** + * Disable warnings when a benchmark accesses module export getters too many times. + * @default false +@@ -3611,7 +3611,7 @@ interface InlineConfig { + * Specifies the memory limit for `worker_thread` or `child_process` before they are recycled. + * If you see memory leaks, try to tinker this value. + */ +- vmMemoryLimit?: string | number; ++ vmMemoryLimit?: string | number | undefined; + /** + * Pool used to run tests in. + * diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..64c2b44 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,29 @@ +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests/e2e', + testMatch: '**/*.spec.ts', + fullyParallel: true, + forbidOnly: Boolean(process.env['CI']), + retries: 0, + workers: 2, + timeout: 60_000, + reporter: [['list'], ['html', { open: 'never' }]], + use: { + baseURL: 'http://127.0.0.1:4177', + serviceWorkers: 'block', + trace: 'retain-on-failure', + }, + projects: [ + { name: 'chromium', use: { browserName: 'chromium' } }, + { name: 'firefox', use: { browserName: 'firefox' } }, + { name: 'webkit', use: { browserName: 'webkit' } }, + ], + webServer: { + command: 'node tests/e2e/server.ts', + url: 'http://127.0.0.1:4177/health', + reuseExistingServer: false, + timeout: 10_000, + gracefulShutdown: { signal: 'SIGTERM', timeout: 1_000 }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..e68b9c9 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1482 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.3.4 + version: 12.3.4 + +packages: + + '@pnpm/exe.darwin-arm64@12.3.4': + resolution: {integrity: sha512-PAyUol8T1+/+ViOiXAt51ECA+QnfXCqz6foL4bW+LsoX0NcVd5XVEM2mRQu+LV4oc7uRz9zf9U0P+XFfuQeDAw==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.3.4': + resolution: {integrity: sha512-fxP9JCk0Cdye+ePuj+GJJLMUMTqHGWRdb1dtv4How876uQ2ehxvenpgiYAir/ceO9PsYUZkFTtyZdx+rRu5QOA==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.linux-arm64-musl@12.3.4': + resolution: {integrity: sha512-FBOt0/7ye6O6q4AllVV5QMviB6qE6fqkeczV/+MDWQsmo+QJrlfsh6X7CpH/tClVpBZEyIbjpUoT8bNhCYBxEg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.3.4': + resolution: {integrity: sha512-t71AVA7LRqiKTyZ5xMYaZc2n5DfdpMbfokZuiIOXHBOM03ECnF0t4iYwaBDqJgVjlKYUOwaF/bRQajGNA4cJ4w==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.3.4': + resolution: {integrity: sha512-RPmk7Jb/aYaFvL2iyDN/AtMY+hUEsue732WmXpcuQ9tBpMnGyA5py7Z3+e+qmQaJ0zY/4ni9jJiyPBQHujmv6w==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.3.4': + resolution: {integrity: sha512-2ZqOlSPkfwX1h5cR+FPiWf8+F+2hZT/3TvhUK5sigHqwaQCIiq8R7CGxhndKs63JtcLi2a1Qpo+wX/EoyfjyJQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.3.4': + resolution: {integrity: sha512-ANyrHqyqco6SXBysUTRF74itDyyraea7IbFsKFdNXTjcFnfycTDx37EwuhdpPYFNSIh2JhUG4fByclsRfiHX7w==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.3.4': + resolution: {integrity: sha512-WH/KqBPY/hq2Tb7SgQltEZytimcjgKRaCRL/aM9CI0c67iKc5TVmHUhIiL3Ux9FB4bWn36i6XewUcScQI+zG8w==} + cpu: [x64] + os: [win32] + + pnpm@12.3.4: + resolution: {integrity: sha512-lhqkH7B32joEpEHZ+OFevAyW2o73ELLrZ7+e58sGEOq9SPH9hfUc/+c4RnhfoPh8VqOocqHYk/hEZ0G1zORUVw==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.darwin-arm64@12.3.4': + optional: true + + '@pnpm/exe.darwin-x64@12.3.4': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.3.4': + optional: true + + '@pnpm/exe.linux-arm64@12.3.4': + optional: true + + '@pnpm/exe.linux-x64-musl@12.3.4': + optional: true + + '@pnpm/exe.linux-x64@12.3.4': + optional: true + + '@pnpm/exe.win32-arm64@12.3.4': + optional: true + + '@pnpm/exe.win32-x64@12.3.4': + optional: true + + pnpm@12.3.4: + optionalDependencies: + '@pnpm/exe.darwin-arm64': 12.3.4 + '@pnpm/exe.darwin-x64': 12.3.4 + '@pnpm/exe.linux-arm64': 12.3.4 + '@pnpm/exe.linux-arm64-musl': 12.3.4 + '@pnpm/exe.linux-x64': 12.3.4 + '@pnpm/exe.linux-x64-musl': 12.3.4 + '@pnpm/exe.win32-arm64': 12.3.4 + '@pnpm/exe.win32-x64': 12.3.4 + +--- +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +patchedDependencies: + vitest@5.0.0: b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c + +importers: + + .: + devDependencies: + '@playwright/test': + specifier: 1.63.0 + version: 1.63.0 + '@types/node': + specifier: 26.4.1 + version: 26.4.1 + '@vitest/browser-playwright': + specifier: 5.0.0 + version: 5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.4.1))(vitest@5.0.0) + '@vitest/coverage-v8': + specifier: 5.0.0 + version: 5.0.0(@vitest/browser@5.0.0)(vitest@5.0.0) + oxlint: + specifier: 1.81.0 + version: 1.81.0 + playwright: + specifier: 1.63.0 + version: 1.63.0 + typescript: + specifier: 7.0.2 + version: 7.0.2 + vite: + specifier: 8.2.2 + version: 8.2.2(@types/node@26.4.1) + vitest: + specifier: 5.0.0 + version: 5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) + +packages: + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@blazediff/core@1.10.0': + resolution: {integrity: sha512-AOQff0zgR7cGsZL+4E7hVkmujoPUpm0J9xzWGWZj5wCjd3gmxESXAPfKyuzs93VdpQNFhHlBhfOjrcZ+XTERtQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@oxc-project/types@0.148.0': + resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==} + + '@oxlint/binding-android-arm-eabi@1.81.0': + resolution: {integrity: sha512-IcCRsXiedJoJopY6mpZUBEeVFsUrutmrG7dZ87zMuKJlhg70Ora9bBl1WcCxZQtyI10YpnVdEso5oCg7YcfSHw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.81.0': + resolution: {integrity: sha512-GRrIPyTGVhx3L3h+0T5xT2A0jFAcdPv4+IfuXpGDLIdl6XeYhgg/zw72A5ILZoUgRqZuM8F1y+V/gfDriXSxzQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.81.0': + resolution: {integrity: sha512-qNQ9tXRgLuKbqSV1S2h9h4KPHjbovO7RRR2/enUOtHzTkFZ7B9X5zqqHJua8dRyc7dBy7Aoyq5pqTSLFVcAzGQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.81.0': + resolution: {integrity: sha512-q0QTm32jWga2Gv4j7IaVZN0jYMi9UV73sWVgFtDA4iIfqwMCLLZ3ve+9KwfYtsaKZSgQhmPaogeZWqDZpcY1Pw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.81.0': + resolution: {integrity: sha512-/+8wVWDXEC7wHVAhOc59Fw/SkMc1arLkFD8iQCaSsmzenK1X4doFqquL9H1wrtGUzaiycVqkf/sSpcILK6W1UA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.81.0': + resolution: {integrity: sha512-4xt422FEgioRq9hAL4Tq7fujGUWnc8z1BJ+Oi8RN8vB8axaP+sdK6a2xdlcQCCYnJg9QMuMFS0AucuIFx/EacA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.81.0': + resolution: {integrity: sha512-u3vna8KdGplH4DRCW9K54D68fcMo7IxVrkCJWwXnIhwtBdnDnYrmzOUA/XjmBlPpcLsgw9Z5BNdY4za9+Dj+MQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.81.0': + resolution: {integrity: sha512-3j9k+gsYsE7nv71GWotXsqsa2l9/aJenD7dVHNt/CBvsb0SgRjSMnHFeP59IXUAl1wvVFhqGl2wJNMwWU3UBlA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-arm64-musl@1.81.0': + resolution: {integrity: sha512-k5iAp3dNxW0/uDCBY+WSm8jKB2szu7SkEQZdgRRpDXvuDd69vvDcqhB3A/pWCfCwXyenjNjFn9Td1fVoyAc+Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-ppc64-gnu@1.81.0': + resolution: {integrity: sha512-TFqLja3uYmVSte6nof9GWrex9Z8WgdZrNiLC6Te5rXGDqXB2y4j/26iFhwosXiAFqDhE9JJVuuCkDKLwptTn1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-gnu@1.81.0': + resolution: {integrity: sha512-UEcySvGS0NOVo7h7n7CYyJL9+6gFAh7Zc/ToDXVScFvzHSTIxtzkMVU30rmQ6+nQ1LF+UdiRDdJajpDu+OylLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-musl@1.81.0': + resolution: {integrity: sha512-H+diDbhD00+wI1IRP8Kz88x/lat+DgtoBJzoTthS16xkTJGNaEkfb8gzmd1rzc/2uDQQMl7GNl+JFUacVeWxIA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-s390x-gnu@1.81.0': + resolution: {integrity: sha512-8znJ/5TekjOKg1j1Acho4PJMdiAHLtlcXuWEiipOhAMV6rQcXdmDdXCbheyDczN6TjBwiNfjcP81k4AthrKRzw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-gnu@1.81.0': + resolution: {integrity: sha512-Q2Wj70yFsvn5QjlmifFzbj4H+kJy53bwqc41o1fzoM7MpLV1NIbhg/LpWXRfC6KOkSAdUx1Wd8VJsdPmhp/HRA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-musl@1.81.0': + resolution: {integrity: sha512-cPInHp/ddEe5qkyK2IiyQ8Q3Mp2oLLEhhsGgTK2oZx4L6+llGam1H1yBvJZ7qHfOXj8N3hxBS8sj4tO+gtFlIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxlint/binding-openharmony-arm64@1.81.0': + resolution: {integrity: sha512-0CQxSX4ajqm07AHBf5U33qQzXKdd7wtq/oTL/7vpY6RNNuxrRi8W4bqUV1Jyu/vj+9KmxQyDhxfeVX1nQL6kfg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.81.0': + resolution: {integrity: sha512-l0hbeISm9673hVrrQU8j/p2M7YH9Ouoj7p7E/QM55NTrKVLP+P3PF8hLu+OY+x0VtGRW+ggiQKZqmdYps9H+TA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.81.0': + resolution: {integrity: sha512-ksqPP5jbFXcYreEQ7zdJh06rJQBymCTyGRCdaXjfcf2aG4f8KxUWY5wcgYHmaTK+FJ4bPG5sUAdOX+6trnH1JA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.81.0': + resolution: {integrity: sha512-IZuUCwGw9emG5JtCp+fYGB+Z4OWEoeEcM8R5BA1pYw63/ieYFVdcU2ylxTpHbVHSenZnsYE+ZZ20uHAJszQ4cA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@playwright/test@1.63.0': + resolution: {integrity: sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==} + engines: {node: '>=20'} + hasBin: true + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@rolldown/binding-android-arm-eabi@1.2.7': + resolution: {integrity: sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.7': + resolution: {integrity: sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.7': + resolution: {integrity: sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.7': + resolution: {integrity: sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.7': + resolution: {integrity: sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.7': + resolution: {integrity: sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.7': + resolution: {integrity: sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.7': + resolution: {integrity: sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.7': + resolution: {integrity: sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.7': + resolution: {integrity: sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.7': + resolution: {integrity: sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.7': + resolution: {integrity: sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.7': + resolution: {integrity: sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.7': + resolution: {integrity: sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.7': + resolution: {integrity: sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/node@26.4.1': + resolution: {integrity: sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA==} + + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + + '@vitest/browser-playwright@5.0.0': + resolution: {integrity: sha512-N+gED9y4/8pypaHjz/x0ah3CjoBr+N0hWWT+Gq4VXtMzyB+rUIdHni0ulzpD1j4H77iWy5Jg8PRMlVn6kjxo6g==} + peerDependencies: + playwright: '*' + vitest: 5.0.0 + + '@vitest/browser@5.0.0': + resolution: {integrity: sha512-JC9FG5xIRxPHXJPcdCaluIJcEoeM0IwGQ3xneuJk09LXKHRNs40BqWDWymQikbb20yOpYvzpKzmYgPRuSzKmvg==} + peerDependencies: + vitest: 5.0.0 + + '@vitest/coverage-v8@5.0.0': + resolution: {integrity: sha512-toMg6PZGCIa/lQNCDoASrfb1ly4hsUKXFtFYC9kD4t78o5Y6LyNJU7AENt8eHPr3quYdxaxK7hj2mnbFfUk9NA==} + peerDependencies: + '@vitest/browser': 5.0.0 + vitest: 5.0.0 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/istanbul-lib-coverage@1.0.1': + resolution: {integrity: sha512-k3DJZ8LhMBK9NS4SclF1ASD3OgXEWDorbIcPTRDK0/Zae6fRvu+fJRxtFdLfHsa9Y24beCdPnoNZ4LviTNstfA==} + engines: {node: '>=22'} + + '@vitest/istanbul-lib-report@1.0.1': + resolution: {integrity: sha512-1EOLRfsTMnyAr3+kEAsP4o9dhaDlGPpD7H5iLBBeq//YpNB1VIahkPhB+eRp9N2Dkfw8oySROjE3yf9XDeaIkQ==} + engines: {node: '>=22'} + + '@vitest/mocker@5.0.0': + resolution: {integrity: sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@5.0.0': + resolution: {integrity: sha512-PVRNuB3wpReb4SQEs4zTKM4KWFhQ5pw3spE8naoDJNB5T5aWRzGKHwXcLUllr0WeOTXpB6bSr3CJLo5+7XQSSQ==} + + '@vitest/spy@5.0.0': + resolution: {integrity: sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g==} + + '@vitest/ui@5.0.0': + resolution: {integrity: sha512-h2FIFwggCY2GxUd2UdQoYNVQkOIqEQLPhNREcl3FUiRsdzQep7NWwYbSmhGEA9nFLPDq5pXzRMcBZQU8Py83sg==} + peerDependencies: + vitest: 5.0.0 + + '@vitest/utils@5.0.0': + resolution: {integrity: sha512-dO++xL3vDfvhTAVimfkuQUA3k+JClIF1i1vAkPqpcGAthRmeWnXmHB7YPViPvgCwviX8u7Y5W1u2N//AaQr3fw==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-v8-to-istanbul@1.0.5: + resolution: {integrity: sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + magic-string@1.2.3: + resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==} + + magicast@0.5.4: + resolution: {integrity: sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + + oxlint@1.81.0: + resolution: {integrity: sha512-HyrJYqeoOCL0iqaLEzGewGT48ZX99P3hxYh8udAF9RGGIghSamkXE4ClUyBpEDNqasamThgmlPbuMOe7SAZmHg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=7.0.2001' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + playwright-core@1.63.0: + resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==} + engines: {node: '>=20'} + hasBin: true + + playwright@1.63.0: + resolution: {integrity: sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==} + engines: {node: '>=20'} + hasBin: true + + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + rolldown@1.2.7: + resolution: {integrity: sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + + tinybench@6.1.4: + resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==} + engines: {node: '>=20.0.0'} + + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} + hasBin: true + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + vite@8.2.2: + resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 || ^0.5.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@5.0.0: + resolution: {integrity: sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.0 + '@vitest/browser-preview': 5.0.0 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.0 + '@vitest/coverage-v8': 5.0.0 + '@vitest/ui': 5.0.0 + happy-dom: '*' + jsdom: '*' + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + +snapshots: + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bcoe/v8-coverage@1.0.2': {} + + '@blazediff/core@1.10.0': {} + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@oxc-project/types@0.148.0': {} + + '@oxlint/binding-android-arm-eabi@1.81.0': + optional: true + + '@oxlint/binding-android-arm64@1.81.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.81.0': + optional: true + + '@oxlint/binding-darwin-x64@1.81.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.81.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.81.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.81.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.81.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.81.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.81.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.81.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.81.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.81.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.81.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.81.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.81.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.81.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.81.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.81.0': + optional: true + + '@playwright/test@1.63.0': + dependencies: + playwright: 1.63.0 + + '@polka/url@1.0.0-next.29': {} + + '@rolldown/binding-android-arm-eabi@1.2.7': + optional: true + + '@rolldown/binding-android-arm64@1.2.7': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.7': + optional: true + + '@rolldown/binding-darwin-x64@1.2.7': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.7': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.7': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.7': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.7': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.7': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.7': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.7': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.7': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.7': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.7': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.7': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/node@26.4.1': + dependencies: + undici-types: 8.3.0 + + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true + + '@vitest/browser-playwright@5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.4.1))(vitest@5.0.0)': + dependencies: + '@vitest/browser': 5.0.0(vite@8.2.2(@types/node@26.4.1))(vitest@5.0.0) + '@vitest/mocker': 5.0.0(vite@8.2.2(@types/node@26.4.1)) + playwright: 1.63.0 + tinyrainbow: 3.1.1 + vitest: 5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@5.0.0(vite@8.2.2(@types/node@26.4.1))(vitest@5.0.0)': + dependencies: + '@blazediff/core': 1.10.0 + '@vitest/mocker': 5.0.0(vite@8.2.2(@types/node@26.4.1)) + '@vitest/ui': 5.0.0(vitest@5.0.0) + '@vitest/utils': 5.0.0 + magic-string: 1.2.3 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.1.1 + vitest: 5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) + ws: 8.21.3 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/coverage-v8@5.0.0(@vitest/browser@5.0.0)(vitest@5.0.0)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/istanbul-lib-coverage': 1.0.1 + '@vitest/istanbul-lib-report': 1.0.1 + ast-v8-to-istanbul: 1.0.5 + magicast: 0.5.4 + obug: 2.1.4 + std-env: 4.2.0 + tinyrainbow: 3.1.1 + vitest: 5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) + optionalDependencies: + '@vitest/browser': 5.0.0(vite@8.2.2(@types/node@26.4.1))(vitest@5.0.0) + + '@vitest/istanbul-lib-coverage@1.0.1': {} + + '@vitest/istanbul-lib-report@1.0.1': + dependencies: + '@vitest/istanbul-lib-coverage': 1.0.1 + + '@vitest/mocker@5.0.0(vite@8.2.2(@types/node@26.4.1))': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.0 + estree-walker: 3.0.3 + magic-string: 1.2.3 + optionalDependencies: + vite: 8.2.2(@types/node@26.4.1) + + '@vitest/pretty-format@5.0.0': + dependencies: + tinyrainbow: 3.1.1 + + '@vitest/spy@5.0.0': {} + + '@vitest/ui@5.0.0(vitest@5.0.0)': + dependencies: + '@vitest/utils': 5.0.0 + fflate: 0.8.3 + flatted: 3.4.4 + pathe: 2.0.3 + sirv: 3.0.2 + tinyrainbow: 3.1.1 + vitest: 5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) + + '@vitest/utils@5.0.0': + dependencies: + '@vitest/pretty-format': 5.0.0 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + + assertion-error@2.0.1: {} + + ast-v8-to-istanbul@1.0.5: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + + chai@6.2.2: {} + + convert-source-map@2.0.0: {} + + detect-libc@2.1.2: {} + + es-module-lexer@2.3.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + expect-type@1.4.0: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fflate@0.8.3: {} + + flatted@3.4.4: {} + + fsevents@2.3.3: + optional: true + + js-tokens@10.0.0: {} + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + magic-string@1.2.3: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + magicast@0.5.4: + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + source-map-js: 1.2.1 + + mrmime@2.0.1: {} + + nanoid@3.3.18: {} + + obug@2.1.4: {} + + oxlint@1.81.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.81.0 + '@oxlint/binding-android-arm64': 1.81.0 + '@oxlint/binding-darwin-arm64': 1.81.0 + '@oxlint/binding-darwin-x64': 1.81.0 + '@oxlint/binding-freebsd-x64': 1.81.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.81.0 + '@oxlint/binding-linux-arm-musleabihf': 1.81.0 + '@oxlint/binding-linux-arm64-gnu': 1.81.0 + '@oxlint/binding-linux-arm64-musl': 1.81.0 + '@oxlint/binding-linux-ppc64-gnu': 1.81.0 + '@oxlint/binding-linux-riscv64-gnu': 1.81.0 + '@oxlint/binding-linux-riscv64-musl': 1.81.0 + '@oxlint/binding-linux-s390x-gnu': 1.81.0 + '@oxlint/binding-linux-x64-gnu': 1.81.0 + '@oxlint/binding-linux-x64-musl': 1.81.0 + '@oxlint/binding-openharmony-arm64': 1.81.0 + '@oxlint/binding-win32-arm64-msvc': 1.81.0 + '@oxlint/binding-win32-ia32-msvc': 1.81.0 + '@oxlint/binding-win32-x64-msvc': 1.81.0 + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + playwright-core@1.63.0: {} + + playwright@1.63.0: + dependencies: + playwright-core: 1.63.0 + + pngjs@7.0.0: {} + + postcss@8.5.28: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + rolldown@1.2.7: + dependencies: + '@oxc-project/types': 0.148.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.7 + '@rolldown/binding-android-arm64': 1.2.7 + '@rolldown/binding-darwin-arm64': 1.2.7 + '@rolldown/binding-darwin-x64': 1.2.7 + '@rolldown/binding-freebsd-x64': 1.2.7 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.7 + '@rolldown/binding-linux-arm64-gnu': 1.2.7 + '@rolldown/binding-linux-arm64-musl': 1.2.7 + '@rolldown/binding-linux-ppc64-gnu': 1.2.7 + '@rolldown/binding-linux-s390x-gnu': 1.2.7 + '@rolldown/binding-linux-x64-gnu': 1.2.7 + '@rolldown/binding-linux-x64-musl': 1.2.7 + '@rolldown/binding-openharmony-arm64': 1.2.7 + '@rolldown/binding-win32-arm64-msvc': 1.2.7 + '@rolldown/binding-win32-x64-msvc': 1.2.7 + + siginfo@2.0.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + tinybench@6.1.4: {} + + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + tinyrainbow@3.1.1: {} + + totalist@3.0.1: {} + + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + undici-types@8.3.0: {} + + vite@8.2.2(@types/node@26.4.1): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.28 + rolldown: 1.2.7 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.4.1 + fsevents: 2.3.3 + + vitest@5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)): + dependencies: + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.0(vite@8.2.2(@types/node@26.4.1)) + chai: 6.2.2 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 1.2.3 + obug: 2.1.4 + picomatch: 4.0.7 + std-env: 4.2.0 + tinybench: 6.1.4 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + vite: 8.2.2(@types/node@26.4.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 26.4.1 + '@vitest/browser-playwright': 5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.4.1))(vitest@5.0.0) + '@vitest/coverage-v8': 5.0.0(@vitest/browser@5.0.0)(vitest@5.0.0) + '@vitest/ui': 5.0.0(vitest@5.0.0) + transitivePeerDependencies: + - msw + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + ws@8.21.3: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..0704ca8 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +patchedDependencies: + vitest@5.0.0: patches/vitest@5.0.0.patch diff --git a/scripts/benchmark-compare.ts b/scripts/benchmark-compare.ts new file mode 100644 index 0000000..aa3608b --- /dev/null +++ b/scripts/benchmark-compare.ts @@ -0,0 +1,2 @@ +import { main } from '../benchmarks/cli.ts'; +try { await main(true); } catch (error) { console.error(error); process.exitCode = 1; } diff --git a/scripts/benchmark-memory-worker.ts b/scripts/benchmark-memory-worker.ts new file mode 100644 index 0000000..5e84921 --- /dev/null +++ b/scripts/benchmark-memory-worker.ts @@ -0,0 +1,25 @@ +import { readPeakRss } from '../benchmarks/rss.ts'; +import { loadCatalog } from '../benchmarks/catalog.ts'; +import { loadImplementation, loadUpstream } from '../benchmarks/harness.ts'; +import { commandMetrics, replayCommands } from '../benchmarks/validation.ts'; + +const startupRssBytes = process.memoryUsage().rss; +const startupPeak = readPeakRss(); +const [modulePath, format, fixtureId] = process.argv.slice(2); +if (modulePath === undefined || (format !== 'tuple' && format !== 'legacy' && format !== 'upstream')) throw new Error('Invalid RSS worker arguments.'); +const solver = format === 'upstream' ? loadUpstream(modulePath) : await loadImplementation(modulePath, format, 'RSS worker'); +const loadedRssBytes = process.memoryUsage().rss; +const loadedPeak = readPeakRss(); +const catalog = loadCatalog(); const selectedIds = fixtureId === undefined ? null : new Set(fixtureId.split(',')); +const locks = selectedIds === null ? catalog : catalog.filter((lock) => selectedIds.has(lock.id)); +if (locks.length === 0) throw new Error('RSS fixture not found.'); +for (const lock of locks) { + const commands = solver.normalize(solver.solve(structuredClone(lock.definition))); + if (commands === null || replayCommands(lock.definition, commands).some((position) => position !== 4)) throw new Error('RSS worker solution did not reach target.'); + if (solver.requiresOptimality && commandMetrics(commands).A !== lock.expectedActions) throw new Error('RSS worker action regression.'); +} +const finalPeak = readPeakRss(); +process.stdout.write(JSON.stringify({ status: 'passed', startupRssBytes, loadedRssBytes, + peakRssBytes: finalPeak.peakRssBytes, peakRssMetric: finalPeak.metric, startupPeakRssBytes: startupPeak.peakRssBytes, loadedPeakRssBytes: loadedPeak.peakRssBytes, + resourceUsageMaxRssBytes: finalPeak.resourceUsageMaxRssBytes, startupResourceUsageMaxRssBytes: startupPeak.resourceUsageMaxRssBytes, + resourceUsageLaunchFloorDetected: startupPeak.resourceUsageMaxRssBytes > startupPeak.peakRssBytes, lockCount: locks.length })); diff --git a/scripts/benchmark-memory.ts b/scripts/benchmark-memory.ts new file mode 100644 index 0000000..70b57de --- /dev/null +++ b/scripts/benchmark-memory.ts @@ -0,0 +1,41 @@ +import { randomUUID } from 'node:crypto'; +import { readFileSync } from 'node:fs'; +import { mkdir, mkdtemp, rename, rm, writeFile } from 'node:fs/promises'; +import { basename, dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { collectMetadata, parseBenchmarkArgs } from '../benchmarks/cli.ts'; +import { jsonFile, loadCatalog, sha256 } from '../benchmarks/catalog.ts'; +import { collectMemory, type MemorySpec } from '../benchmarks/memory.ts'; +import { record } from '../benchmarks/validation.ts'; + +const [priorPath, ...args] = process.argv.slice(2); +if (priorPath === undefined || priorPath.startsWith('--')) throw new Error('Usage: node scripts/benchmark-memory.ts PRIOR_BENCHMARK_JSON [--reference PATH] [--output-dir PATH]'); +const options = parseBenchmarkArgs(args); +const metadata = collectMetadata(options); +const previous = record(jsonFile(priorPath)); +if (record(record(previous['metadata'])['candidate'])['moduleSha256'] !== metadata.candidate.moduleSha256) throw new Error('Candidate bytes differ from the earlier timing report; run a full new comparison.'); +const specs: MemorySpec[] = [ { name: 'candidate', module: options.candidateModule, format: 'tuple' }, + { name: 'baseline', module: options.baselineModule, format: options.baselineFormat }, + { name: 'originalBfs', module: fileURLToPath(new URL('../benchmarks/baseline/src/index.mjs', import.meta.url)), format: 'legacy' } ]; +if (options.reference !== undefined) specs.push({ name: 'upstream', module: options.reference, format: 'upstream' }); +const catalog = loadCatalog(); const locks = options.smoke ? catalog.slice(0, 2) : catalog; +const memory = collectMemory(locks, specs, options.memoryRepetitions, (line) => process.stderr.write(`${line}\n`)); +const createdAt = new Date().toISOString(); +const correction = { schemaVersion: 1, createdAt, scope: 'memory-only-correction', + supersedes: { report: resolve(priorPath), sha256: sha256(readFileSync(priorPath)), component: 'memory', + reason: 'Earlier raw getrusage peaks retained a pre-exec launch floor. Its memory pass is invalid; timing and deterministic quality remain unchanged.' }, metadata, memory }; +const outputRoot = resolve(options.outputDir); await mkdir(outputRoot, { recursive: true }); +const staged = await mkdtemp(join(outputRoot, '.memory-')); +const directory = join(outputRoot, `memory-${createdAt.replaceAll(/[^0-9TZ]/g, '')}-${randomUUID().slice(0, 8)}`); +const lines = ['# Corrected isolated RSS evidence', '', `Memory verdict: **${memory.performance.verdict}**. This supplement supersedes the earlier report’s memory measurements and memory pass. Its timing and deterministic quality observations remain valid.`, '', + memory.metric, '', memory.accuracy, '', '| Implementation | Median peak RSS (MiB) | p95 (MiB) |', '| --- | ---: | ---: |']; +for (const [name, measurement] of Object.entries(memory.wholeCatalog)) lines.push(`| ${name} | ${(measurement.peakRssBytes.median / 1048576).toFixed(2)} | ${(measurement.peakRssBytes.p95 / 1048576).toFixed(2)} |`); +lines.push('', `Method: [Linux kernel /proc documentation](${memory.sources[0]}) and [Node resourceUsage documentation](${memory.sources[1]}). RSS remains approximate process memory, including runtime and measurement infrastructure; browser peak memory is unavailable.`, '', 'The JSON contains every individual peak, startup/current RSS, raw getrusage diagnostic, control sample, bounded repeat, source and artifact hash.', ''); +try { + await writeFile(join(staged, 'memory.json'), `${JSON.stringify(correction, null, 2)}\n`, { flag: 'wx' }); + await writeFile(join(staged, 'memory.md'), lines.join('\n'), { flag: 'wx' }); + await rename(staged, directory); +} catch (error) { await rm(staged, { recursive: true, force: true }); throw error; } +const notice = `# Superseded memory measurements\n\nThe memory measurements and memory pass in this run are invalid: raw Node getrusage maxRSS retained a pre-exec launch high-water. The original hashed files remain unchanged for audit. Use [the corrected RSS supplement](../${basename(directory)}/memory.md) and its full JSON. Timing and deterministic quality observations are unaffected.\n`; +await writeFile(join(dirname(resolve(priorPath)), 'MEMORY-SUPERSEDED.md'), notice, { flag: 'wx' }); +console.log(JSON.stringify({ directory, verdict: memory.performance.verdict, supersedes: correction.supersedes }, null, 2)); diff --git a/scripts/benchmark-snapshot.ts b/scripts/benchmark-snapshot.ts new file mode 100644 index 0000000..8c26e4f --- /dev/null +++ b/scripts/benchmark-snapshot.ts @@ -0,0 +1,29 @@ +import { existsSync, readFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { catalogSha256, git, loadCatalog, sha256, verifySource } from '../benchmarks/catalog.ts'; +import { string } from '../benchmarks/validation.ts'; +import { loadImplementation, runComparison, writeSnapshot } from '../benchmarks/harness.ts'; +const fields = new Map(); +const args = process.argv.slice(2); +for (let index = 0; index < args.length; index += 2) { + const key = args[index]; const value = args[index + 1]; + if (key === undefined || value === undefined || !['--source-module', '--source-format', '--reason', '--output'].includes(key) || fields.has(key)) throw new Error('Invalid snapshot arguments.'); + fields.set(key, value); +} +const modulePath = fields.get('--source-module'); const format = fields.get('--source-format') ?? 'tuple'; +const reason = fields.get('--reason'); const output = fields.get('--output'); +if (modulePath === undefined || reason === undefined || reason.trim() === '' || output === undefined || (format !== 'tuple' && format !== 'legacy')) throw new Error('Usage: node scripts/benchmark-snapshot.ts --source-module PATH --source-format tuple|legacy --reason TEXT --output NEW_PATH'); +const solver = await loadImplementation(modulePath, format, 'Snapshot source'); +const report = runComparison({ locks: loadCatalog(), candidate: solver, baseline: solver, repetitions: 2, warmups: 1 }); +const sourceDirectory = dirname(dirname(resolve(modulePath))); +const sourceRevision = existsSync(resolve(sourceDirectory, 'manifest.json')) + ? string(verifySource(sourceDirectory)['revision']) + : git(dirname(resolve(modulePath)), ['rev-parse', 'HEAD']); +const snapshot = { schemaVersion: 1, fixtureSha256: catalogSha256, + source: { revision: sourceRevision, moduleSha256: sha256(readFileSync(modulePath)), format }, reason, + locks: report.results.map((row) => { + const value = row.solvers['candidate']; if (value === undefined) throw new Error('Missing snapshot solution.'); + return { id: row.id, optimumA: row.optimumA, A: value.A, U: value.U, C: value.C, plateSwitches: value.plateSwitches, commands: value.commands }; + }) }; +await writeSnapshot(output, snapshot); +console.log(JSON.stringify({ snapshotPath: resolve(output), lockCount: snapshot.locks.length, source: snapshot.source })); diff --git a/scripts/benchmark.mjs b/scripts/benchmark.mjs deleted file mode 100644 index 64df4f5..0000000 --- a/scripts/benchmark.mjs +++ /dev/null @@ -1,27 +0,0 @@ -import { loadCatalog } from '../benchmarks/catalog.mjs'; -import { collectMetadata, defaultImplementations, loadReference, parseBenchmarkArgs, runAndPublish } from '../benchmarks/harness.mjs'; - -try { - const options = parseBenchmarkArgs(process.argv.slice(2)); - if (options.help) { - console.log('Usage: node scripts/benchmark.mjs [--repetitions 5] [--warmups 1] [--output-dir docs/benchmarks] [--reference /path/to/unlockmyloot] [--smoke]'); - console.log('Reports are published together in a new run-* directory. --smoke uses the first two catalog entries and labels the report as a subset.'); - } else { - const reference = options.reference ? loadReference(options.reference) : null; - const metadata = collectMetadata(reference?.metadata); - const catalog = loadCatalog(); - const result = await runAndPublish({ - locks: options.smoke ? catalog.slice(0, 2) : catalog, - implementations: defaultImplementations(reference?.implementation), - repetitions: options.repetitions, warmups: options.warmups, - metadata, outputDir: options.outputDir, - onProgress: ({ completed, total, id }) => process.stderr.write(`[${completed}/${total}] ${id}: correctness gates passed\n`), - }); - console.log(JSON.stringify({ correctness: result.report.correctness.passed, - lockCount: result.report.config.lockCount, jsonPath: result.jsonPath, - markdownPath: result.markdownPath, comparisons: result.report.comparisons }, null, 2)); - } -} catch (error) { - console.error(`Benchmark failed: ${error.message}`); - process.exitCode = 1; -} diff --git a/scripts/benchmark.ts b/scripts/benchmark.ts new file mode 100644 index 0000000..a703120 --- /dev/null +++ b/scripts/benchmark.ts @@ -0,0 +1,2 @@ +import { main } from '../benchmarks/cli.ts'; +try { await main(false); } catch (error) { console.error(error); process.exitCode = 1; } diff --git a/scripts/browser/worker.mjs b/scripts/browser/worker.mjs deleted file mode 100644 index 8379d30..0000000 --- a/scripts/browser/worker.mjs +++ /dev/null @@ -1,9 +0,0 @@ -import { solveLock } from '../../src/index.mjs'; - -self.onmessage = ({ data }) => { - try { - self.postMessage({ result: solveLock(data) }); - } catch (error) { - self.postMessage({ error: { name: error.name, message: error.message } }); - } -}; diff --git a/scripts/build-browser.mjs b/scripts/build-browser.mjs deleted file mode 100644 index 31595b5..0000000 --- a/scripts/build-browser.mjs +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env node -import { readFile, writeFile, mkdir } from 'node:fs/promises'; -import { createHash } from 'node:crypto'; -import { dirname, join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { Script } from 'node:vm'; - -const PROJECT_ROOT = fileURLToPath(new URL('../', import.meta.url)); -const ARTIFACT = 'dist/gothic-lock-solver.js'; - -// This is a deliberately narrow assembler for our own core, not a JS bundler. -// Dependency order and supported source syntax are explicit and fail closed. -export const BROWSER_MODULES = Object.freeze([ - 'src/lock-definition.mjs', - 'src/state-codec.mjs', - 'src/transition.mjs', - 'src/result.mjs', - 'src/search-limits.mjs', - 'src/search-bfs.mjs', - 'src/matrix-analysis.mjs', - 'src/matrix-search.mjs', - 'src/index.mjs', -]); - -const IDENTIFIER = '[A-Za-z_$][A-Za-z0-9_$]*'; -const NAMES = `${IDENTIFIER}(?:\\s*,\\s*${IDENTIFIER})*`; -const NAMED_IMPORT = new RegExp(`^import \\{\\s*(${NAMES})\\s*\\} from '([^']+)';$`); -const NAMED_REEXPORT = new RegExp(`^export \\{\\s*(${NAMES})\\s*\\} from '([^']+)';$`); -const DECLARATION = new RegExp(`^export (function|class) (${IDENTIFIER})\\b`); - -function transformModule(name, source, completed) { - const exported = new Set(); - const lines = source.split('\n').map((line) => { - const imported = line.match(NAMED_IMPORT); - const reexported = line.match(NAMED_REEXPORT); - if (imported || reexported) { - const [, list, specifier] = imported || reexported; - const names = list.split(',').map((value) => value.trim()); - if (!/^\.\/[a-z][a-z0-9-]*\.mjs$/.test(specifier)) { - throw new Error(`Unsupported dependency in ${name}: ${specifier}`); - } - const dependency = `src/${specifier.slice(2)}`; - const available = completed.get(dependency); - if (!BROWSER_MODULES.includes(dependency) || !available) { - throw new Error(`Unsupported dependency or allowlist order in ${name}: ${specifier}`); - } - for (const identifier of names) { - if (!available.has(identifier)) throw new Error(`Unsupported missing export ${identifier} from ${dependency}`); - if (reexported) { - if (exported.has(identifier)) throw new Error(`Unsupported duplicate export ${identifier} in ${name}`); - exported.add(identifier); - } - } - return `const { ${names.join(', ')} } = modules[${JSON.stringify(dependency)}];`; - } - const declaration = line.match(DECLARATION); - if (declaration) { - const identifier = declaration[2]; - if (exported.has(identifier)) throw new Error(`Unsupported duplicate export ${identifier} in ${name}`); - exported.add(identifier); - return line.slice('export '.length); - } - return line; - }); - const body = lines.join('\n'); - // Deliberately conservative: even unsupported module keywords in comments or - // strings must be reviewed. Dynamic import and import.meta are also rejected. - if (/\b(?:import|export)\b/.test(body)) throw new Error(`Unsupported module syntax in ${name}`); - if (exported.size === 0) throw new Error(`Unsupported module without explicit exports: ${name}`); - completed.set(name, exported); - return `// ${name}\nmodules[${JSON.stringify(name)}] = (() => {\n${body}\nreturn Object.freeze({ ${[...exported].join(', ')} });\n})();`; -} - -/** Generate or verify the ready script. Nothing is written before validation. */ -export async function buildBrowserBundle({ root = PROJECT_ROOT, check = false } = {}) { - const sources = await Promise.all(BROWSER_MODULES.map(async (name) => ({ - name, - source: (await readFile(join(root, name), 'utf8')).replaceAll('\r\n', '\n'), - }))); - const license = (await readFile(join(root, 'LICENSE'), 'utf8')).replaceAll('\r\n', '\n').trimEnd(); - if (license.includes('*/')) throw new Error('Unsupported comment terminator in LICENSE'); - const completed = new Map(); - const modules = sources.map(({ name, source }) => transformModule(name, source, completed)); - const hash = createHash('sha256'); - for (const { name, source } of sources) hash.update(`${name}\0${source}\0`); - hash.update(`LICENSE\0${license}\0`); - const output = `/*!\n * gothic-lock-solver — standalone classic browser script\n * Project attribution: https://github.com/Marcus-Rise/gothic-lock-solver\n * SPDX-License-Identifier: Apache-2.0\n * Generated by scripts/build-browser.mjs. Do not edit this artifact directly.\n * Source SHA-256 (ordered UTF-8/LF modules and LICENSE): ${hash.digest('hex')}\n * No runtime dependencies, module imports, network access, or generated code.\n *\n${license.split('\n').map((line) => ` * ${line}`).join('\n')}\n */\n(function () {\n'use strict';\nconst modules = Object.create(null);\n\n${modules.join('\n\n')}\n\nglobalThis.GothicLockSolver = modules["src/index.mjs"];\n})();\n`; - // A classic-script parse catches unsupported declarations and binding errors. - // It does not execute source, and the artifact never invokes eval/Function. - new Script(output, { filename: ARTIFACT }); - const destination = join(root, ARTIFACT); - if (check) { - let current; - try { current = await readFile(destination, 'utf8'); } - catch (error) { if (error.code !== 'ENOENT') throw error; } - if (current !== output) throw new Error(`${ARTIFACT} is missing or out of date; run node scripts/build-browser.mjs`); - } else { - await mkdir(dirname(destination), { recursive: true }); - await writeFile(destination, output); - } - return output; -} - -if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - try { - const args = process.argv.slice(2); - if (args.length > 1 || (args.length === 1 && args[0] !== '--check')) { - throw new Error('Usage: node scripts/build-browser.mjs [--check]'); - } - await buildBrowserBundle({ check: args[0] === '--check' }); - process.stdout.write(`${args[0] === '--check' ? 'Verified' : 'Created'} ${ARTIFACT}\n`); - } catch (error) { - process.stderr.write(`${error.message}\n`); - process.exitCode = 1; - } -} diff --git a/scripts/build.ts b/scripts/build.ts new file mode 100644 index 0000000..f7435e4 --- /dev/null +++ b/scripts/build.ts @@ -0,0 +1,31 @@ +import { createHash } from 'node:crypto'; +import { builtinModules } from 'node:module'; +import { execFileSync } from 'node:child_process'; +import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; +import { build } from 'vite'; + +const root = fileURLToPath(new URL('../', import.meta.url)); +const dist = new URL('../dist/', import.meta.url); +await rm(dist, { recursive: true, force: true }); +await mkdir(dist, { recursive: true }); +for (const mode of ['readable', 'minified']) { + await build({ root, configFile: fileURLToPath(new URL('../vite.config.ts', import.meta.url)), mode }); +} +await build({ + root, configFile: false, build: { + target: 'node22', outDir: 'dist', emptyOutDir: false, minify: false, sourcemap: false, + lib: { entry: fileURLToPath(new URL('../cli/main.ts', import.meta.url)), formats: ['es'], fileName: () => 'gothic-lock-solver.cli.mjs' }, + rolldownOptions: { external: [...builtinModules, ...builtinModules.map((name) => `node:${name}`)], output: { codeSplitting: false, comments: { legal: false } } }, + }, +}); +execFileSync(process.execPath, [fileURLToPath(new URL('../node_modules/typescript/bin/tsc', import.meta.url)), '-p', 'tsconfig.build.json'], { cwd: root, stdio: 'inherit' }); +await writeFile(new URL('global.d.ts', dist), `/** Types for the classic GothicLockSolver global. */\nexport as namespace GothicLockSolver;\nexport * from './index.js';\n`); +const files: Record = {}; +for (const filename of (await readdir(dist)).sort()) { + files[filename] = createHash('sha256').update(await readFile(new URL(filename, dist))).digest('hex'); +} +const metadata: unknown = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')); +if (typeof metadata !== 'object' || metadata === null || !('version' in metadata) || typeof metadata.version !== 'string') throw new Error('Package version is missing.'); +const sourceSha = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim(); +await writeFile(new URL('manifest.json', dist), `${JSON.stringify({ schemaVersion: 1, package: 'gothic-lock-solver', version: metadata.version, sourceSha, files }, null, 2)}\n`); diff --git a/scripts/ci-baseline.ts b/scripts/ci-baseline.ts new file mode 100644 index 0000000..07758bb --- /dev/null +++ b/scripts/ci-baseline.ts @@ -0,0 +1,67 @@ +import { execFileSync } from 'node:child_process'; +import { appendFile, mkdir, readFile, stat, writeFile } from 'node:fs/promises'; +import { join, resolve } from 'node:path'; +import { assertDigest, previousStableVersion, record, sha256, string, validateManifest } from './release-lib.ts'; +import { writeJson } from './release-files.ts'; +import { fetchBytes, verifyRegistry } from './release-network.ts'; + +const [mode, directoryArgument, outputArgument] = process.argv.slice(2); +if (!['checkout', 'release'].includes(mode ?? '') || !directoryArgument || !outputArgument) throw new Error('Usage: node scripts/ci-baseline.ts checkout BASE_CHECKOUT OUTPUT_DIR | release CANDIDATE_VERSION OUTPUT_DIR'); +const output = resolve(outputArgument); +await mkdir(output, { recursive: true }); + +async function emit(fields: Readonly>): Promise { + const text = Object.entries(fields).map(([key, value]) => `${key}=${value}\n`).join(''); + if (process.env['GITHUB_OUTPUT']) await appendFile(process.env['GITHUB_OUTPUT'], text); + console.log(JSON.stringify(fields)); +} + +if (mode === 'checkout') { + const base = resolve(directoryArgument); + const legacyModule = join(base, 'src/index.mjs'); + const legacy = await stat(legacyModule).then(value => value.isFile(), () => false); + const module = legacy ? legacyModule : join(base, 'dist/gothic-lock-solver.mjs'); + const format = legacy ? 'legacy' : 'tuple'; + const snapshot = join(output, 'baseline-snapshot.json'); + // The supplied base checkout, never a candidate-owned expected snapshot, supplies the live baseline. + execFileSync(process.execPath, ['scripts/benchmark-snapshot.ts', '--source-module', module, '--source-format', format, + '--reason', 'Fresh snapshot from the separately checked-out PR target/reference commit; candidate snapshot is not read.', '--output', snapshot], { stdio: 'inherit' }); + await emit({ available: 'true', module, snapshot, format }); +} else { + const response = await fetch('https://registry.npmjs.org/gothic-lock-solver', { signal: AbortSignal.timeout(30_000) }); + if (response.status === 404) { + await emit({ available: 'false', reason: 'No npm package yet; use the explicitly pinned reference checkout.' }); + } else { + if (!response.ok) throw new Error(`npm baseline metadata HTTP ${response.status}`); + const metadata: unknown = await response.json(); + const versions = record(record(metadata, 'package metadata')['versions'], 'versions'); + const previous = previousStableVersion(Object.keys(versions), directoryArgument); + if (previous === null) { + await emit({ available: 'false', reason: 'No preceding stable version; use the explicitly pinned reference checkout.' }); + } else { + const prefix = `https://github.com/Marcus-Rise/gothic-lock-solver/releases/download/v${previous}/`; + const manifestBytes = await fetchBytes(`${prefix}release-manifest.json`); + const manifestJson: unknown = JSON.parse(manifestBytes.toString('utf8')); + const manifest = validateManifest(manifestJson); + if (manifest.version !== previous || manifest.kind !== 'stable') throw new Error('Previous stable release identity mismatch'); + await verifyRegistry(manifest); + const moduleBytes = await fetchBytes(`${prefix}gothic-lock-solver.mjs`); + assertDigest(moduleBytes, string(manifest.assets['gothic-lock-solver.mjs'], 'previous ESM hash'), 'previous release module'); + const evidenceBytes = await fetchBytes(`${prefix}benchmark-evidence.json`); + assertDigest(evidenceBytes, string(manifest.assets['benchmark-evidence.json'], 'previous evidence hash'), 'previous release evidence'); + const evidenceJson: unknown = JSON.parse(evidenceBytes.toString('utf8')); + const evidence = record(evidenceJson, 'previous evidence'); + const snapshot = record(evidence['qualitySnapshot'], 'previous quality snapshot'); + const source = record(snapshot['source'], 'previous snapshot source'); + if (source['revision'] !== manifest.sourceSha || source['moduleSha256'] !== sha256(moduleBytes)) throw new Error('Previous release snapshot source differs from published module'); + const module = join(output, 'baseline.mjs'); const snapshotPath = join(output, 'baseline-snapshot.json'); + await writeFile(module, moduleBytes, { flag: 'wx' }); + await writeFile(join(output, 'previous-release-manifest.json'), manifestBytes, { flag: 'wx' }); + await writeFile(join(output, 'previous-benchmark-evidence.json'), evidenceBytes, { flag: 'wx' }); + await writeJson(snapshotPath, { ...snapshot, source: { ...source, releaseVersion: previous, kind: 'previous-stable-release' } }); + // Re-read the bytes that will actually be imported, after writing the evidence chain. + assertDigest(await readFile(module), string(source['moduleSha256'], 'snapshot module hash'), 'saved baseline module'); + await emit({ available: 'true', module, snapshot: snapshotPath, format: 'tuple', version: previous }); + } + } +} diff --git a/scripts/release-browser.ts b/scripts/release-browser.ts new file mode 100644 index 0000000..44db6ec --- /dev/null +++ b/scripts/release-browser.ts @@ -0,0 +1,49 @@ +import { createServer } from 'node:http'; +import { chromium, firefox, webkit } from 'playwright'; +import { loadCatalog } from '../benchmarks/catalog.ts'; +import { replayCommands, tupleCommands } from '../benchmarks/validation.ts'; +import { runtimeFiles, type ReleaseManifest } from './release-lib.ts'; + +/** Real CDN imports, in each supported engine, after byte/header verification. */ +export async function verifyCdnBrowsers(manifest: ReleaseManifest): Promise { + const locks = loadCatalog(); + const server = createServer((_request, response) => { + response.writeHead(200, { 'Content-Type': 'text/html' }); + response.end('Gothic Lock Solver CDN verification'); + }); + await new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', resolve); }); + const address = server.address(); + if (address === null || typeof address === 'string') throw new Error('Local CDN test origin unavailable'); + try { + for (const engine of [chromium, firefox, webkit]) { + const browser = await engine.launch(); + try { + for (const filename of runtimeFiles) { + const page = await browser.newPage(); + try { + await page.goto(`http://127.0.0.1:${address.port}/`); + const url = `https://cdn.jsdelivr.net/npm/${manifest.name}@${manifest.version}/dist/${filename}`; + const runtimeRequests: string[] = []; + page.on('request', request => { if (request.url().startsWith('https://')) runtimeRequests.push(request.url()); }); + if (filename.endsWith('.js')) await page.addScriptTag({ url }); + const results: unknown = await page.evaluate(async ({ moduleUrl, moduleFormat, definitions }) => { + const namespace: unknown = moduleFormat ? await import(moduleUrl) : Reflect.get(globalThis, 'GothicLockSolver'); + if (typeof namespace !== 'object' || namespace === null || !('solveLock' in namespace) || typeof namespace.solveLock !== 'function') throw new Error('Missing CDN solveLock export'); + const solve = namespace.solveLock; + return definitions.map((definition): unknown => Reflect.apply(solve, undefined, [definition.state, definition.links])); + }, { moduleUrl: url, moduleFormat: filename.endsWith('.mjs'), definitions: locks.map(lock => lock.definition) }); + if (!Array.isArray(results) || results.length !== 45) throw new Error('CDN fixture results are incomplete'); + for (const [index, result] of results.entries()) { + const lock = locks[index]; + if (!lock) throw new Error('Unexpected CDN fixture'); + const commands = tupleCommands(result); + if (commands.length !== lock.expectedActions || replayCommands(lock.definition, commands).some(position => position !== 4)) throw new Error(`CDN fixture mismatch: ${engine.name()}/${filename}/${lock.id}`); + } + if (runtimeRequests.length !== 1 || runtimeRequests[0] !== url) throw new Error(`Additional CDN runtime dependency: ${runtimeRequests.join(', ')}`); + console.log(`CDN browser passed: ${engine.name()}, ${filename}, 45 fixtures`); + } finally { await page.close(); } + } + } finally { await browser.close(); } + } + } finally { await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve())); } +} diff --git a/scripts/release-files.ts b/scripts/release-files.ts new file mode 100644 index 0000000..84a6688 --- /dev/null +++ b/scripts/release-files.ts @@ -0,0 +1,118 @@ +import { execFileSync } from 'node:child_process'; +import { mkdir, readFile, writeFile } from 'node:fs/promises'; +import { join, resolve } from 'node:path'; +import { isDeepStrictEqual } from 'node:util'; +import { assertDigest, parseReleaseVersion, record, releaseAssets, sha256, string, validateManifest, validateEvidenceGate, type ReleaseKind, type ReleaseManifest } from './release-lib.ts'; + +export async function readJson(path: string): Promise { + const value: unknown = JSON.parse(await readFile(path, 'utf8')); + return value; +} + +export async function writeJson(path: string, value: unknown): Promise { + await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, { flag: 'wx' }); +} + +function archiveFile(tarball: string, filename: string): Buffer { + return execFileSync('tar', ['-xOf', tarball, `package/${filename}`], { maxBuffer: 16 * 1024 * 1024 }); +} + +export interface EvidenceAcceptance { + readonly acceptedEvidenceSha256?: string; + readonly acceptanceReason?: string; +} + +export async function verifyPreparedRelease(directory: string, acceptance: EvidenceAcceptance = {}): Promise { + const manifest = validateManifest(await readJson(join(directory, 'release-manifest.json'))); + for (const filename of releaseAssets) assertDigest(await readFile(join(directory, filename)), string(manifest.assets[filename], filename), filename); + const tarball = resolve(directory, 'package.tgz'); + const packed: unknown = JSON.parse(archiveFile(tarball, 'package.json').toString('utf8')); + const metadata = record(packed, 'packed metadata'); + if (metadata['name'] !== manifest.name || metadata['version'] !== manifest.version || metadata['private'] === true) throw new Error('Packed package identity differs from release'); + const build: unknown = JSON.parse(archiveFile(tarball, 'dist/manifest.json').toString('utf8')); + const buildManifest = record(build, 'packed build manifest'); + if (buildManifest['sourceSha'] !== manifest.sourceSha || buildManifest['version'] !== manifest.version) throw new Error('Packed build source/version differs from release'); + for (const filename of releaseAssets.filter(name => /\.(?:mjs|js)$/.test(name))) { + assertDigest(archiveFile(tarball, `dist/${filename}`), string(manifest.assets[filename], filename), `packed ${filename}`); + } + // The publishing-side verifier receives acceptance from its trusted caller, never from an artifact. + await verifyBenchmarkEvidence(directory, manifest.sourceSha, string(manifest.assets['gothic-lock-solver.mjs'], 'prepared ESM hash'), + acceptance.acceptedEvidenceSha256, acceptance.acceptanceReason); + return manifest; +} + +export interface PrepareOptions { + readonly root: string; + readonly outputDir: string; + readonly benchmarkDir: string; + readonly sourceSha: string; + readonly kind: ReleaseKind; + readonly acceptedEvidenceSha256?: string; + readonly acceptanceReason?: string; +} + +export async function verifyBenchmarkEvidence(directory: string, sourceSha: string, moduleSha256: string, acceptedSha256 = '', acceptanceReason = ''): Promise { + const bytes = await readFile(join(directory, 'benchmark-evidence.json')); + const evidence = record(await readJson(join(directory, 'benchmark-evidence.json')), 'benchmark evidence'); + const acceptance = validateEvidenceGate(evidence, sha256(bytes), acceptedSha256, acceptanceReason); + const metadata = record(evidence['metadata'], 'benchmark metadata'); + const candidate = record(metadata['candidate'], 'benchmark candidate'); + if (candidate['revision'] !== sourceSha || candidate['moduleSha256'] !== moduleSha256 || candidate['format'] !== 'tuple') throw new Error('Benchmark candidate differs from prepared source/artifact'); + const reports = record(evidence['reports'], 'benchmark report hashes'); + for (const [field, filename] of [['json', 'benchmark.json'], ['markdown', 'benchmark.md']]) { + if (!field || !filename) throw new Error('Missing report path'); + const report = record(reports[field], field); + if (report['path'] !== filename) throw new Error('Unexpected benchmark report path'); + assertDigest(await readFile(join(directory, filename)), string(report['sha256'], 'report hash'), filename); + } + const report = record(await readJson(join(directory, 'benchmark.json')), 'detailed benchmark report'); + if (record(report['correctness'], 'report correctness')['passed'] !== true) throw new Error('Detailed report correctness must pass'); + for (const field of ['schemaVersion', 'quality', 'performance', 'config', 'metadata', 'memory']) { + if (!isDeepStrictEqual(report[field], evidence[field])) throw new Error(`Detailed report ${field} differs from benchmark evidence`); + } + return acceptance; +} + +/** Copies the already tested archive; this function never invokes a build or npm pack. */ +export async function prepareRelease(options: PrepareOptions): Promise { + const build = record(await readJson(join(options.root, 'dist/manifest.json')), 'build manifest'); + const metadata = record(await readJson(join(options.root, 'package.json')), 'package metadata'); + const version = string(metadata['version'], 'package version'); + if (parseReleaseVersion(version).prerelease !== (options.kind === 'canary')) throw new Error('Stable/canary version mismatch'); + if (build['sourceSha'] !== options.sourceSha || build['version'] !== version) throw new Error('Build source/version differs from requested release'); + const moduleSha256 = sha256(await readFile(join(options.root, 'dist/gothic-lock-solver.mjs'))); + const acceptance = await verifyBenchmarkEvidence(options.benchmarkDir, options.sourceSha, moduleSha256, options.acceptedEvidenceSha256, options.acceptanceReason); + const assets: Record = {}; + await mkdir(options.outputDir, { recursive: true }); + for (const filename of releaseAssets) { + const from = filename === 'package.tgz' ? join(options.root, 'artifacts/package/package.tgz') + : filename.startsWith('benchmark') ? join(options.benchmarkDir, filename) + : join(options.root, 'dist', filename); + const bytes = await readFile(from); + assets[filename] = sha256(bytes); + await writeFile(join(options.outputDir, filename), bytes, { flag: 'wx' }); + } + const manifest = validateManifest({ schemaVersion: 1, name: 'gothic-lock-solver', version, sourceSha: options.sourceSha, kind: options.kind, + tarballSha256: assets['package.tgz'], assets }); + await writeJson(join(options.outputDir, 'release-manifest.json'), manifest); + await verifyPreparedRelease(options.outputDir, options); + const evidence = record(await readJson(join(options.benchmarkDir, 'benchmark-evidence.json')), 'release evidence'); + const evidenceMetadata = record(evidence['metadata'], 'evidence metadata'); + const baseline = record(evidenceMetadata['baseline'] ?? {}, 'evidence baseline'); + const memoryPerformance = record(record(evidence['memory'], 'memory evidence')['performance'], 'memory verdict'); + const cdn = `https://cdn.jsdelivr.net/npm/${manifest.name}@${manifest.version}/dist`; + const notes = [ + `# Gothic Lock Solver ${manifest.version}`, '', `Source: ${manifest.sourceSha}.`, '', + `[npm ${manifest.version}](https://www.npmjs.com/package/${manifest.name}/v/${manifest.version})`, '', + `Install: \`npm install ${manifest.name}@${manifest.version}\`.`, '', + 'This release provides the two-argument tuple API: solveLock(state, links). Commands are [zero-based index, signed numeric pin delta]. See README for migration from the legacy result wrapper.', '', + `Benchmark quality: **passed**, 45 fixtures. Performance: **${string(evidence['performance'], 'performance')}**; memory: **${string(memoryPerformance['verdict'], 'memory verdict')}**.`, + `Baseline: ${typeof baseline['kind'] === 'string' ? baseline['kind'] : 'reference/previous release'}, source ${typeof baseline['revision'] === 'string' ? baseline['revision'] : 'see benchmark-evidence.json'}. Detailed report hashes and the comparison source are preserved in benchmark-evidence.json.`, + acceptance === null ? 'The calibrated timing and memory gates passed.' : `Accepted performance evidence: ${acceptance}`, '', + '```html', ``, '', '```', '', + '```html', '', '```', '', + 'All five executable files, the exact tested npm archive, checksum manifest and detailed benchmark reports are attached.', '', + ].join('\n'); + await writeFile(join(options.outputDir, 'release-notes.md'), notes, { flag: 'wx' }); + return manifest; +} diff --git a/scripts/release-lib.ts b/scripts/release-lib.ts new file mode 100644 index 0000000..1a1dd37 --- /dev/null +++ b/scripts/release-lib.ts @@ -0,0 +1,106 @@ +import { createHash } from 'node:crypto'; + +export const runtimeFiles = ['gothic-lock-solver.mjs', 'gothic-lock-solver.min.mjs', 'gothic-lock-solver.js', 'gothic-lock-solver.min.js'] as const; +export const releaseAssets = [...runtimeFiles, 'gothic-lock-solver.cli.mjs', 'benchmark-evidence.json', 'benchmark.json', 'benchmark.md', 'package.tgz'] as const; +export type ReleaseKind = 'stable' | 'canary'; + +export interface ReleaseManifest { + readonly schemaVersion: 1; + readonly name: 'gothic-lock-solver'; + readonly version: string; + readonly sourceSha: string; + readonly kind: ReleaseKind; + readonly tarballSha256: string; + readonly assets: Readonly>; +} + +export function record(value: unknown, context: string): Record { + if (value === null || typeof value !== 'object' || Array.isArray(value)) throw new Error(`Invalid ${context}`); + return Object.fromEntries(Object.entries(value)); +} + +export function string(value: unknown, context: string): string { + if (typeof value !== 'string' || value.length === 0) throw new Error(`Invalid ${context}`); + return value; +} + +export function sha256(value: Uint8Array | string): string { + return createHash('sha256').update(value).digest('hex'); +} + +export function assertDigest(bytes: Uint8Array, expected: string, context: string): void { + if (!/^[a-f0-9]{64}$/.test(expected) || sha256(bytes) !== expected) throw new Error(`${context} identity mismatch; existing versions/assets must never be overwritten`); +} + +export function parseReleaseVersion(version: string): { version: string; prerelease: boolean } { + const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/.exec(version); + if (!match || match[4]?.split('.').some(part => /^\d+$/.test(part) && part.length > 1 && part.startsWith('0'))) throw new Error(`Invalid release SemVer: ${version}`); + return { version, prerelease: match[4] !== undefined }; +} + +export function canaryVersion(base: string, sourceSha: string, runId: string, attempt: string): string { + if (parseReleaseVersion(base).prerelease || !/^[a-f0-9]{40}$/.test(sourceSha) || !/^[1-9]\d*$/.test(runId) || !/^[1-9]\d*$/.test(attempt)) throw new Error('Canary requires stable base version, source SHA and positive run identifiers'); + return `${base}-canary.${runId}.${attempt}.${sourceSha.slice(0, 12)}`; +} + +export function previousStableVersion(versions: readonly string[], current: string): string | null { + const currentIsCanary = parseReleaseVersion(current).prerelease; + const compare = (left: string, right: string): number => { + const a = left.split('.')[Symbol.iterator](); const b = right.split('.')[Symbol.iterator](); + for (let index = 0; index < 3; index += 1) { + const first = BigInt(string(a.next().value, 'version component')); + const second = BigInt(string(b.next().value, 'version component')); + if (first !== second) return first < second ? -1 : 1; + } + return 0; + }; + const base = current.split('-')[0]; + if (base === undefined) throw new Error('Missing version base'); + return versions.filter(version => !parseReleaseVersion(version).prerelease && compare(version, base) < (currentIsCanary ? 1 : 0)).sort(compare).at(-1) ?? null; +} + +export function shouldPromoteGithubLatest(kind: ReleaseKind, version: string, npmLatest: string | null, publishedStableVersions: readonly string[]): boolean { + if (kind !== 'stable' || version !== npmLatest) return false; + if (parseReleaseVersion(version).prerelease) throw new Error('Stable latest must be a stable version'); + return !publishedStableVersions.some(published => previousStableVersion([version], published) === version); +} + +export function assertReleaseSource(kind: ReleaseKind, ref: string, event: string, repository: string, sourceRepository: string): void { + if (!repository || repository !== sourceRepository || !ref.startsWith('refs/heads/')) throw new Error('Release must use a branch in the owner repository'); + if (kind === 'stable' && (ref !== 'refs/heads/main' || !['push', 'workflow_dispatch'].includes(event))) throw new Error('Stable releases require main push or explicit main resume'); + if (kind === 'canary' && event !== 'workflow_dispatch') throw new Error('Canaries require workflow_dispatch'); +} + +export function validateManifest(value: unknown): ReleaseManifest { + const input = record(value, 'release manifest'); + if (input['schemaVersion'] !== 1 || input['name'] !== 'gothic-lock-solver') throw new Error('Unsupported release manifest'); + const version = string(input['version'], 'version'); + const sourceSha = string(input['sourceSha'], 'source SHA'); + const kind = input['kind']; + const tarballSha256 = string(input['tarballSha256'], 'tarball hash'); + if (!/^[a-f0-9]{40}$/.test(sourceSha) || !/^[a-f0-9]{64}$/.test(tarballSha256) || (kind !== 'stable' && kind !== 'canary')) throw new Error('Invalid release identity'); + if (parseReleaseVersion(version).prerelease !== (kind === 'canary')) throw new Error('Release kind and version disagree'); + const rawAssets = record(input['assets'], 'asset hashes'); + if (Object.keys(rawAssets).length !== releaseAssets.length) throw new Error('Incomplete or unexpected release assets'); + const assets: Record = {}; + for (const filename of releaseAssets) { + const hash = string(rawAssets[filename], `hash for ${filename}`); + if (!/^[a-f0-9]{64}$/.test(hash)) throw new Error(`Invalid asset hash: ${filename}`); + assets[filename] = hash; + } + if (assets['package.tgz'] !== tarballSha256) throw new Error('Tarball hash disagrees with asset manifest'); + return { schemaVersion: 1, name: 'gothic-lock-solver', version, sourceSha, kind, tarballSha256, assets }; +} + +export function validateEvidenceGate(value: unknown, evidenceSha256: string, acceptedSha256 = '', acceptanceReason = ''): string | null { + const evidence = record(value, 'benchmark evidence'); + if (evidence['schemaVersion'] !== 1 || evidence['quality'] !== 'passed') throw new Error('Release requires passed benchmark quality'); + if (record(evidence['config'], 'benchmark config')['lockCount'] !== 45) throw new Error('Release requires all 45 fixtures'); + const memory = record(evidence['memory'], 'memory evidence'); + const memoryVerdict = record(memory['performance'], 'memory performance')['verdict']; + const timing = evidence['performance']; + if (![timing, memoryVerdict].every(verdict => ['passed', 'inconclusive', 'regression'].includes(string(verdict, 'performance verdict')))) throw new Error('Unknown performance verdict'); + if (timing === 'passed' && memoryVerdict === 'passed') return null; + if (!/^[a-f0-9]{64}$/.test(evidenceSha256) || acceptedSha256 !== evidenceSha256 || acceptanceReason.trim().length === 0) throw new Error('Release performance needs a passed result or explicit acceptance of this exact evidence SHA with a reason'); + return acceptanceReason; +} diff --git a/scripts/release-network.ts b/scripts/release-network.ts new file mode 100644 index 0000000..a6807d7 --- /dev/null +++ b/scripts/release-network.ts @@ -0,0 +1,119 @@ +import { execFileSync } from 'node:child_process'; +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; +import { setTimeout } from 'node:timers/promises'; +import { assertDigest, record, releaseAssets, runtimeFiles, sha256, shouldPromoteGithubLatest, string, type ReleaseManifest } from './release-lib.ts'; + +export async function fetchBytes(url: string): Promise { + const response = await fetch(url, { signal: AbortSignal.timeout(30_000) }); + if (!response.ok) throw new Error(`HTTP ${response.status}: ${url}`); + return Buffer.from(await response.arrayBuffer()); +} + +export async function registryMetadata(version: string): Promise | null> { + const response = await fetch(`https://registry.npmjs.org/gothic-lock-solver/${encodeURIComponent(version)}`, { signal: AbortSignal.timeout(30_000) }); + if (response.status === 404) return null; + if (!response.ok) throw new Error(`npm registry HTTP ${response.status}`); + const value: unknown = await response.json(); + return record(value, 'npm metadata'); +} + +export async function verifyRegistry(manifest: ReleaseManifest): Promise { + const metadata = await registryMetadata(manifest.version); + if (!metadata || metadata['name'] !== manifest.name || metadata['version'] !== manifest.version) throw new Error('Exact npm version is not available'); + const tarballUrl = new URL(string(record(metadata['dist'], 'npm dist')['tarball'], 'npm tarball URL')); + if (tarballUrl.origin !== 'https://registry.npmjs.org' || !tarballUrl.pathname.startsWith('/gothic-lock-solver/-/')) throw new Error('Unexpected npm tarball origin/path'); + assertDigest(await fetchBytes(tarballUrl.href), manifest.tarballSha256, 'published npm tarball'); +} + +export async function publishOrResumeNpm(directory: string, manifest: ReleaseManifest): Promise { + const existing = await registryMetadata(manifest.version); + if (existing) { + await verifyRegistry(manifest); + await recordDeliveryStatus(directory, 'npm-existing-verified', 'Resuming exact existing npm bytes; no npm publish was invoked.'); + } else { + await recordDeliveryStatus(directory, 'npm-publish-started', 'If publication succeeds and later delivery fails, rerun the failed jobs using this same prepared artifact.'); + execFileSync('npm', ['publish', resolve(directory, 'package.tgz'), '--access', 'public', '--tag', manifest.kind === 'stable' ? 'latest' : 'canary', '--provenance', '--ignore-scripts', '--registry', 'https://registry.npmjs.org/'], { stdio: 'inherit' }); + await recordDeliveryStatus(directory, 'npm-published-awaiting-verification', 'npm publication completed; registry/CDN and GitHub release remain pending.'); + } +} + +/** Retries only delivery verification; publication is never retried here. */ +export async function retryDelivery(action: () => Promise): Promise { + let lastError: unknown; + for (let attempt = 0; attempt < 4; attempt += 1) { + try { await action(); return; } catch (error) { lastError = error; } + if (attempt < 3) await setTimeout(5_000 * (attempt + 1)); + } + throw lastError; +} + +export async function verifyCdn(manifest: ReleaseManifest): Promise { + for (const filename of runtimeFiles) { + const url = `https://cdn.jsdelivr.net/npm/${manifest.name}@${manifest.version}/dist/${filename}`; + const response = await fetch(url, { headers: { Origin: 'https://example.com' }, signal: AbortSignal.timeout(30_000) }); + if (!response.ok || !/(?:java|ecma)script/i.test(response.headers.get('content-type') ?? '') || response.headers.get('access-control-allow-origin') !== '*') throw new Error(`CDN delivery headers/status rejected: ${filename}`); + assertDigest(Buffer.from(await response.arrayBuffer()), string(manifest.assets[filename], filename), `CDN ${filename}`); + } +} + +function gh(args: readonly string[]): string { + return execFileSync('gh', [...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 16 * 1024 * 1024 }); +} + +/** Uploads missing assets only. Existing npm versions and GitHub assets are immutable inputs. */ +export async function completeGithubRelease(directory: string, manifest: ReleaseManifest, repository: string): Promise { + if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository)) throw new Error('Invalid repository'); + const tag = `v${manifest.version}`; + // Create the exact tag explicitly: a draft release need not have materialized its tag yet. + const matchingRefs: unknown = JSON.parse(gh(['api', `repos/${repository}/git/matching-refs/tags/${tag}`])); + if (!Array.isArray(matchingRefs)) throw new Error('Invalid tag refs response'); + if (!matchingRefs.map(value => record(value, 'tag ref')).some(value => value['ref'] === `refs/tags/${tag}`)) { + gh(['api', '--method', 'POST', `repos/${repository}/git/refs`, '-f', `ref=refs/tags/${tag}`, '-f', `sha=${manifest.sourceSha}`]); + } + const ref: unknown = JSON.parse(gh(['api', `repos/${repository}/git/ref/tags/${tag}`])); + let object = record(record(ref, 'tag ref')['object'], 'tag object'); + for (let depth = 0; object['type'] === 'tag' && depth < 5; depth += 1) { + const annotated: unknown = JSON.parse(gh(['api', `repos/${repository}/git/tags/${string(object['sha'], 'tag SHA')}`])); + object = record(record(annotated, 'annotated tag')['object'], 'tag target'); + } + if (object['type'] !== 'commit' || object['sha'] !== manifest.sourceSha) throw new Error('Existing release tag does not identify the prepared source'); + const releases: unknown = JSON.parse(gh(['api', `repos/${repository}/releases?per_page=100`])); + if (!Array.isArray(releases)) throw new Error('Invalid releases response'); + const existing = releases.map(value => record(value, 'release')).find(value => value['tag_name'] === tag); + if (!existing) { + gh(['release', 'create', tag, '--repo', repository, '--verify-tag', '--target', manifest.sourceSha, '--draft', '--title', tag, + '--notes-file', join(directory, 'release-notes.md'), ...(manifest.kind === 'canary' ? ['--prerelease', '--latest=false'] : [])]); + } + const release: unknown = JSON.parse(gh(['api', `repos/${repository}/releases/tags/${tag}`])); + const releaseRecord = record(release, 'GitHub release'); + if (releaseRecord['prerelease'] !== (manifest.kind === 'canary')) throw new Error('Existing release kind differs'); + const assets = releaseRecord['assets']; + if (!Array.isArray(assets)) throw new Error('Invalid GitHub assets'); + const expectedFiles = [...releaseAssets, 'release-manifest.json']; + for (const filename of expectedFiles) { + const expectedBytes = await readFile(join(directory, filename)); + const asset = assets.map(value => record(value, 'asset')).find(value => value['name'] === filename); + if (!asset) { + if (releaseRecord['draft'] !== true) throw new Error('Published release has a missing asset; refusing to mutate it'); + gh(['release', 'upload', tag, join(directory, filename), '--repo', repository]); + } + const temp = await mkdtemp(join(tmpdir(), 'gothic-release-asset-')); + try { + gh(['release', 'download', tag, '--repo', repository, '--pattern', filename, '--dir', temp]); + assertDigest(await readFile(join(temp, filename)), sha256(expectedBytes), `GitHub ${filename}`); + } finally { await rm(temp, { recursive: true, force: true }); } + } + if (releaseRecord['draft'] === true) { + const npmLatest = await registryMetadata('latest'); + const publishedStableVersions = releases.map(value => record(value, 'published release')).filter(value => value['draft'] === false && value['prerelease'] === false) + .map(value => string(value['tag_name'], 'release tag')).filter(value => /^v(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/.test(value)).map(value => value.slice(1)); + const latest = shouldPromoteGithubLatest(manifest.kind, manifest.version, npmLatest ? string(npmLatest['version'], 'npm latest version') : null, publishedStableVersions); + gh(['release', 'edit', tag, '--repo', repository, '--draft=false', `--latest=${latest}`]); + } +} + +export async function recordDeliveryStatus(directory: string, stage: string, detail: string): Promise { + await writeFile(join(directory, 'delivery-status.json'), `${JSON.stringify({ stage, detail, recordedAt: new Date().toISOString() }, null, 2)}\n`); +} diff --git a/scripts/release.ts b/scripts/release.ts new file mode 100644 index 0000000..e422fbb --- /dev/null +++ b/scripts/release.ts @@ -0,0 +1,96 @@ +import { execFileSync } from 'node:child_process'; +import { appendFile, readFile, writeFile } from 'node:fs/promises'; +import { resolve } from 'node:path'; +import { assertReleaseSource, canaryVersion, record, string, type ReleaseKind } from './release-lib.ts'; +import { prepareRelease, readJson, verifyPreparedRelease, type EvidenceAcceptance } from './release-files.ts'; +import { completeGithubRelease, publishOrResumeNpm, recordDeliveryStatus, retryDelivery, verifyCdn, verifyRegistry } from './release-network.ts'; + +function env(name: string): string { return string(process.env[name], name); } + +function workflowAcceptance(): EvidenceAcceptance { + return { acceptedEvidenceSha256: process.env['PERFORMANCE_ACCEPTED_EVIDENCE_SHA256'] ?? '', + acceptanceReason: process.env['PERFORMANCE_ACCEPTANCE_REASON'] ?? '' }; +} + +function kind(): ReleaseKind { + const value = process.env['RELEASE_KIND'] ?? 'stable'; + if (value !== 'stable' && value !== 'canary') throw new Error('Invalid RELEASE_KIND'); + return value; +} + +function requirePublicationContext(): void { + if (process.env['NPM_PUBLICATION_READY'] !== 'true' || process.env['GITHUB_ACTIONS'] !== 'true') throw new Error('Publication readiness is not configured; prepare/verify are available locally'); + const repository = env('GITHUB_REPOSITORY'); + if (process.env['GITHUB_REF'] !== 'refs/heads/main' || process.env['GITHUB_WORKFLOW_REF'] !== `${repository}/.github/workflows/release.yml@refs/heads/main`) throw new Error('Privileged release code must come from the main workflow'); + assertReleaseSource(kind(), env('RELEASE_SOURCE_REF'), env('GITHUB_EVENT_NAME'), repository, env('RELEASE_SOURCE_REPOSITORY')); +} + +const [command, ...args] = process.argv.slice(2); +const fields = new Map(); +for (let index = 0; index < args.length; index += 2) { + const key = args[index]; const value = args[index + 1]; + if (!key || !value || !['--directory', '--benchmark-dir', '--source-sha'].includes(key) || fields.has(key)) throw new Error('Invalid release arguments'); + fields.set(key, value); +} +const directory = resolve(fields.get('--directory') ?? 'artifacts/release'); + +try { + switch (command) { + case 'canary-version': { + const path = resolve('package.json'); const metadata = record(await readJson(path), 'package metadata'); + metadata['version'] = canaryVersion(string(metadata['version'], 'package version'), env('RELEASE_SOURCE_SHA'), env('GITHUB_RUN_ID'), env('GITHUB_RUN_ATTEMPT')); + await writeFile(path, `${JSON.stringify(metadata, null, 2)}\n`); + console.log(metadata['version']); + break; + } + case 'prepare': { + const sourceSha = fields.get('--source-sha') ?? execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(); + const manifest = await prepareRelease({ root: process.cwd(), outputDir: directory, + benchmarkDir: resolve(string(fields.get('--benchmark-dir'), '--benchmark-dir')), sourceSha, kind: kind(), + ...workflowAcceptance() }); + console.log(JSON.stringify(manifest)); + if (process.env['GITHUB_OUTPUT']) await appendFile(process.env['GITHUB_OUTPUT'], `version=${manifest.version}\nsource-sha=${manifest.sourceSha}\n`); + break; + } + case 'check': { + console.log(JSON.stringify(await verifyPreparedRelease(directory, workflowAcceptance()))); + break; + } + case 'publish': { + requirePublicationContext(); + const manifest = await verifyPreparedRelease(directory, workflowAcceptance()); + if (manifest.kind !== kind() || manifest.sourceSha !== env('RELEASE_SOURCE_SHA')) throw new Error('Prepared release does not match the verified workflow source'); + if (execFileSync('npm', ['--version'], { encoding: 'utf8' }).trim() !== '12.0.2') throw new Error('Expected pinned npm CLI 12.0.2'); + await publishOrResumeNpm(directory, manifest); + break; + } + case 'verify': { + const manifest = await verifyPreparedRelease(directory, workflowAcceptance()); + await retryDelivery(async () => { await verifyRegistry(manifest); await verifyCdn(manifest); }); + const { verifyCdnBrowsers } = await import('./release-browser.ts'); + await verifyCdnBrowsers(manifest); + await recordDeliveryStatus(directory, 'registry-cdn-verified', 'Exact npm tarball and four CDN files verified; Chromium, Firefox and WebKit passed all 45 fixtures.'); + break; + } + case 'github': { + requirePublicationContext(); + const manifest = await verifyPreparedRelease(directory, workflowAcceptance()); + if (manifest.kind !== kind() || manifest.sourceSha !== env('RELEASE_SOURCE_SHA')) throw new Error('Prepared GitHub release source mismatch'); + if (!execFileSync('gh', ['--version'], { encoding: 'utf8' }).startsWith('gh version 2.100.0 ')) throw new Error('Expected pinned GitHub CLI 2.100.0'); + const status = record(JSON.parse(await readFile(resolve(directory, 'delivery-status.json'), 'utf8')), 'delivery status'); + if (status['stage'] !== 'registry-cdn-verified') throw new Error('GitHub publication requires completed registry/CDN/browser verification'); + await completeGithubRelease(directory, manifest, env('GITHUB_REPOSITORY')); + await recordDeliveryStatus(directory, 'complete', 'npm, CDN browser delivery and GitHub Release verified.'); + break; + } + default: throw new Error('Usage: node scripts/release.ts canary-version|prepare|check|publish|verify|github [--directory PATH] [--benchmark-dir PATH] [--source-sha SHA]'); + } +} catch (error) { + const detail = error instanceof Error ? error.message : String(error); + console.error(detail); + if (['publish', 'verify', 'github'].includes(command ?? '')) { + console.error('Release incomplete. npm may already contain this exact version; preserve prepared artifacts and rerun failed jobs. Never overwrite/unpublish to recover.'); + if (process.env['GITHUB_STEP_SUMMARY']) await appendFile(process.env['GITHUB_STEP_SUMMARY'], `\nRelease incomplete at ${command}. Preserve the prepared artifact and rerun failed jobs. Existing versions/assets are verified, never overwritten.\n`); + } + process.exitCode = 1; +} diff --git a/scripts/verify-package.ts b/scripts/verify-package.ts new file mode 100644 index 0000000..80a8098 --- /dev/null +++ b/scripts/verify-package.ts @@ -0,0 +1,82 @@ +import { strict as assert } from 'node:assert'; +import { execFileSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { copyFile, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const consumerNodes = new Set([process.execPath]); +const args = process.argv.slice(2); +for (let index = 0; index < args.length; index += 2) { + const executable = args[index + 1]; + if (args[index] !== '--node' || executable === undefined || executable.length === 0) { + throw new Error('Usage: pnpm verify:package [--node PATH]...'); + } + consumerNodes.add(executable); +} + +const root = fileURLToPath(new URL('../', import.meta.url)); +const temporary = await mkdtemp(join(tmpdir(), 'gothic-package-consumer-')); +const artifacts = join(root, 'artifacts', 'package'); +const destination = join(artifacts, 'package.tgz'); +const checksumPath = join(artifacts, 'package.sha256'); +const compiler = fileURLToPath(new URL('../node_modules/typescript/bin/tsc', import.meta.url)); +const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; +function run(command: string, args: readonly string[], cwd = temporary): string { + return execFileSync(command, args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'] }); +} +try { + await mkdir(artifacts, { recursive: true }); + // A failed fresh check must not leave a stale archive labeled verified. + await rm(destination, { force: true }); + await rm(checksumPath, { force: true }); + run(npm, ['pack', '--json', '--ignore-scripts', '--pack-destination', temporary], root); + const archives = (await readdir(temporary)).filter((name) => name.endsWith('.tgz')); + const archive = archives[0]; + assert.equal(archives.length, 1, 'Pack must create exactly one tarball'); + assert.ok(archive); + const archivePath = join(temporary, archive); + await writeFile(join(temporary, 'package.json'), JSON.stringify({ name: 'gothic-clean-consumer', version: '1.0.0', private: true, type: 'module' })); + run(npm, ['install', '--offline', '--ignore-scripts', '--no-audit', '--no-fund', archivePath]); + const packageRoot = join(temporary, 'node_modules', 'gothic-lock-solver'); + const packageDist = join(packageRoot, 'dist'); + assert.deepEqual((await readdir(packageRoot)).sort(), ['LICENSE', 'README.md', 'dist', 'package.json']); + const installedMetadata: unknown = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8')); + const distributionManifest: unknown = JSON.parse(await readFile(join(packageDist, 'manifest.json'), 'utf8')); + assert.ok(typeof installedMetadata === 'object' && installedMetadata !== null && 'version' in installedMetadata); + assert.ok(typeof distributionManifest === 'object' && distributionManifest !== null && 'version' in distributionManifest && 'sourceSha' in distributionManifest); + assert.equal(distributionManifest.version, installedMetadata.version, 'Build version is stale'); + assert.equal(distributionManifest.sourceSha, run('git', ['rev-parse', 'HEAD'], root).trim(), 'Build source SHA is stale'); + const repoDist = join(root, 'dist'); + const sourceFiles = (await readdir(repoDist)).sort(); + assert.deepEqual((await readdir(packageDist)).sort(), sourceFiles); + for (const file of sourceFiles) assert.deepEqual(await readFile(join(packageDist, file)), await readFile(join(repoDist, file)), `Pack changed ${file}`); + for (const file of ['consumer.mts', 'consumer.mjs', 'global-consumer.ts']) { + await copyFile(join(root, 'tests', 'distribution', 'fixtures', file), join(temporary, file)); + } + const rawConfig: unknown = JSON.parse(await readFile(join(root, 'tsconfig.json'), 'utf8')); + assert.ok(typeof rawConfig === 'object' && rawConfig !== null && 'compilerOptions' in rawConfig); + assert.ok(typeof rawConfig.compilerOptions === 'object' && rawConfig.compilerOptions !== null); + await writeFile(join(temporary, 'tsconfig.json'), JSON.stringify({ + compilerOptions: { ...rawConfig.compilerOptions, types: [], lib: ['ES2022'], moduleDetection: 'legacy' }, + include: ['consumer.mts', 'global-consumer.ts'], + })); + run(process.execPath, [compiler, '--project', join(temporary, 'tsconfig.json')]); + for (const executable of consumerNodes) { + process.stdout.write(run(executable, [join(temporary, 'consumer.mjs')])); + } + // Reuse byte-preserving HTTP and isolated file consumers against installed + // tarball files. Vite and source resolution are absent from these pages. + execFileSync(process.execPath, [join(root, 'node_modules', 'playwright', 'cli.js'), 'test'], { + cwd: root, + env: { ...process.env, GOTHIC_DIST_DIRECTORY: packageDist }, + stdio: 'inherit', + }); + await copyFile(archivePath, destination); + const sha256 = createHash('sha256').update(await readFile(destination)).digest('hex'); + await writeFile(checksumPath, `${sha256} package.tgz\n`); + console.log(JSON.stringify({ verified: true, node: process.version, tarball: destination, sha256 })); +} finally { + await rm(temporary, { recursive: true, force: true }); +} diff --git a/scripts/verify-web-core.mjs b/scripts/verify-web-core.mjs deleted file mode 100644 index 5522a6f..0000000 --- a/scripts/verify-web-core.mjs +++ /dev/null @@ -1,51 +0,0 @@ -import assert from 'node:assert/strict'; -import { readFile } from 'node:fs/promises'; -import { fileURLToPath } from 'node:url'; -import vm from 'node:vm'; - -// A dependency-boundary check, not a substitute for a real browser run. -// SourceTextModule requires --experimental-vm-modules on supported Node versions. -const root = new URL('../src/', import.meta.url); -const context = vm.createContext(Object.create(null)); -const modules = new Map(); - -async function load(url) { - if (!url.href.startsWith(root.href) || !url.pathname.endsWith('.mjs')) { - throw new Error(`Core import leaves browser-safe source tree: ${url.href}`); - } - if (modules.has(url.href)) return modules.get(url.href); - // Cache the pending read too: multiple importers may request one dependency - // concurrently while the module graph is linking. - const pending = readFile(url, 'utf8').then(source => new vm.SourceTextModule(source, { - context, - identifier: url.href, - })); - modules.set(url.href, pending); - return pending; -} - -const entry = await load(new URL('index.mjs', root)); -await entry.link((specifier, referencingModule) => { - if (!specifier.startsWith('./') && !specifier.startsWith('../')) { - throw new Error(`Core has an external/Node import: ${specifier}`); - } - return load(new URL(specifier, referencingModule.identifier)); -}); -await entry.evaluate(); -assert.equal(vm.runInContext('typeof process', context), 'undefined'); -assert.equal(vm.runInContext('typeof Buffer', context), 'undefined'); -assert.equal(vm.runInContext('typeof require', context), 'undefined'); - -const solve = entry.namespace.solveLock; -const independent = { state: [1, 7], links: [[0, 0], [0, 0]] }; -for (const algorithm of ['matrix-astar', 'bfs']) { - const result = solve(independent, { algorithm }); - assert.equal(result.status, 'solved'); - assert.equal(result.commands.length, 2); - assert.equal(JSON.stringify(result.finalState), '[4,4]'); -} -console.log(JSON.stringify({ - status: 'passed', - environment: 'isolated ECMAScript module context without Node globals', - modules: [...modules.keys()].map(url => fileURLToPath(url).slice(fileURLToPath(root).length)), -})); diff --git a/solve-lock.mjs b/solve-lock.mjs index 5d0856c..277f57c 100755 --- a/solve-lock.mjs +++ b/solve-lock.mjs @@ -1,4 +1,4 @@ #!/usr/bin/env node -import { runCli } from './src/cli.mjs'; +import { runCli } from './cli/cli.ts'; process.exitCode = await runCli(process.argv.slice(2)); diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 0000000..8b7f98b --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,24 @@ +/** Invalid shape, sparse arrays, positions, coefficients or diagonal values. */ +export class LockInputError extends Error { + constructor(message: string, options?: ErrorOptions) { + super(message, options); + this.name = 'LockInputError'; + } +} + +export type SearchLimit = 'maxVisited' | 'maxExpanded' | 'maxFrontier' | 'maxDenseBytes' | 'stateEncoding' | 'matrixArithmetic'; + +/** Computation exhausted a resource; this is never proof of unreachability. */ +export class SearchLimitError extends Error { + readonly limit: SearchLimit; + readonly maximum: number; + readonly used: number | string; + + constructor(limit: SearchLimit, maximum: number, used: number | string, options?: ErrorOptions) { + super(`Превышен ресурсный предел поиска: ${limit} (предел ${maximum}, требуется ${used}).`, options); + this.name = 'SearchLimitError'; + this.limit = limit; + this.maximum = maximum; + this.used = used; + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..be14c3f --- /dev/null +++ b/src/index.ts @@ -0,0 +1,28 @@ +import { LockModel } from './lock-model.ts'; +import { MatrixSearch } from './matrix-search.ts'; +import { PreparedSearch, SearchBudget } from './search-limits.ts'; +import type { Command, Links, Position, State } from './types.ts'; + +/** + * Find a legal sequence with the minimum number of actions, or null when the + * goal is proved unreachable. Each [index, delta] directly changes the selected + * pin's numeric position and applies links[index][target] once without cascades. + * Inputs are copied and never modified; all goal positions are 4. + * + * Fixed tuples must have N >= 2 and matching N×N links. Dynamic arrays undergo + * the same shape and value checks at runtime. + * @throws {LockInputError} When either input violates the lock model. + * @throws {SearchLimitError} When a computation limit is exceeded (not unsolvable). + */ +export function solveLock( + state: S & (number extends S['length'] ? unknown : S extends State ? unknown : never), + links: NoInfer>, +): readonly Command[] | null { + const model = new LockModel(state, links); + const prepared = new PreparedSearch(model); + return new MatrixSearch(model, prepared, new SearchBudget()).solve(); +} + +export { LockInputError, SearchLimitError } from './errors.ts'; +export type { SearchLimit } from './errors.ts'; +export type { Command, Delta, Link, Links, Position, State } from './types.ts'; diff --git a/src/indexed-heap.ts b/src/indexed-heap.ts new file mode 100644 index 0000000..8a4baab --- /dev/null +++ b/src/indexed-heap.ts @@ -0,0 +1,73 @@ +export interface SearchNode { + readonly key: number; + g: number; + readonly h: number; + readonly r: readonly number[]; + previous: SearchNode | null; + plate: number; + delta: number; + readonly sequence: number; + heapIndex: number; + closed: boolean; +} + +/** One entry per open state; decrease-key never leaves obsolete heap nodes. */ +export class IndexedHeap { + private readonly nodes: SearchNode[] = []; + + get size(): number { return this.nodes.length; } + + private before(a: SearchNode, b: SearchNode): boolean { + return a.g + a.h < b.g + b.h || + (a.g + a.h === b.g + b.h && (a.h < b.h || (a.h === b.h && a.sequence < b.sequence))); + } + + up(node: SearchNode): void { + let index = node.heapIndex; + while (index > 0) { + const parent = Math.floor((index - 1) / 2); + const parentNode = this.nodes[parent]; + if (parentNode === undefined) throw new Error('Internal heap parent invariant failed.'); + if (!this.before(node, parentNode)) break; + this.nodes[index] = parentNode; + parentNode.heapIndex = index; + index = parent; + } + this.nodes[index] = node; + node.heapIndex = index; + } + + push(node: SearchNode): void { + node.heapIndex = this.nodes.length; + this.nodes.push(node); + this.up(node); + } + + pop(): SearchNode { + const first = this.nodes[0]; + if (first === undefined) throw new Error('Internal empty-heap invariant failed.'); + const last = this.nodes.pop(); + if (last === undefined) throw new Error('Internal empty-heap invariant failed.'); + first.heapIndex = -1; + if (this.nodes.length === 0) return first; + let index = 0; + while (true) { + let child = index * 2 + 1; + if (child >= this.nodes.length) break; + let childNode = this.nodes[child]; + if (childNode === undefined) throw new Error('Internal heap child invariant failed.'); + if (child + 1 < this.nodes.length) { + const right = this.nodes[child + 1]; + if (right === undefined) throw new Error('Internal heap child invariant failed.'); + if (this.before(right, childNode)) { child += 1; childNode = right; } + } + if (!this.before(childNode, last)) break; + this.nodes[index] = childNode; + childNode.heapIndex = index; + index = child; + } + this.nodes[index] = last; + last.heapIndex = index; + return first; + } +} diff --git a/src/indexed.ts b/src/indexed.ts new file mode 100644 index 0000000..c0a9f55 --- /dev/null +++ b/src/indexed.ts @@ -0,0 +1,24 @@ +/** + * Checked indexed access for internally dense arrays and bounded state tables. + * Call sites establish bounds through validated dimensions, allocated lengths or + * search membership. The check keeps that invariant executable and avoids type + * assertions / unchecked non-null accesses under noUncheckedIndexedAccess. + */ +export function readAt(values: readonly T[], index: number): T { + const value = values[index]; + if (value === undefined) throw new Error(`Internal indexed-access invariant failed at ${index}.`); + return value; +} + +/** Typed readers keep hot typed-array access separate from object-array shapes. */ +export function readByte(values: Uint8Array, index: number): number { + const value = values[index]; + if (value === undefined) throw new Error(`Internal byte-access invariant failed at ${index}.`); + return value; +} + +export function readInt32(values: Int32Array, index: number): number { + const value = values[index]; + if (value === undefined) throw new Error(`Internal integer-access invariant failed at ${index}.`); + return value; +} diff --git a/src/lock-model.ts b/src/lock-model.ts new file mode 100644 index 0000000..6c3f743 --- /dev/null +++ b/src/lock-model.ts @@ -0,0 +1,58 @@ +import { LockInputError } from './errors.ts'; +import type { Link, Position } from './types.ts'; + +function isArray(value: unknown): value is readonly unknown[] { + return Array.isArray(value); +} + +function isPosition(value: unknown): value is Position { + return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 7; +} + +function isLink(value: unknown): value is Link { + return value === -1 || value === 0 || value === 1; +} + +/** Owns a validated immutable copy of the lock, independent of caller mutations. */ +export class LockModel { + readonly state: readonly Position[]; + readonly links: readonly (readonly Link[])[]; + + constructor(state: unknown, links: unknown) { + if (!isArray(state) || state.length < 2) { + throw new LockInputError('state должен содержать не менее 2 пластин.'); + } + const copiedState: Position[] = []; + for (let index = 0; index < state.length; index += 1) { + const position = state[index]; + if (!Object.hasOwn(state, index) || !isPosition(position)) { + throw new LockInputError(`state[${index}] должен быть целым числом от 1 до 7.`); + } + copiedState.push(position); + } + if (!isArray(links) || links.length !== state.length) { + throw new LockInputError(`links должен быть матрицей ${state.length}x${state.length}.`); + } + const copiedLinks: (readonly Link[])[] = []; + for (let source = 0; source < links.length; source += 1) { + const row = links[source]; + if (!Object.hasOwn(links, source) || !isArray(row) || row.length !== state.length) { + throw new LockInputError(`links[${source}] должен содержать ${state.length} элементов.`); + } + const copiedRow: Link[] = []; + for (let target = 0; target < row.length; target += 1) { + const coefficient = row[target]; + if (!Object.hasOwn(row, target) || !isLink(coefficient)) { + throw new LockInputError(`links[${source}][${target}] должен быть -1, 0 или 1.`); + } + if (source === target && coefficient !== 0) { + throw new LockInputError(`links[${source}][${target}] на диагонали должен быть 0.`); + } + copiedRow.push(coefficient); + } + copiedLinks.push(Object.freeze(copiedRow)); + } + this.state = Object.freeze(copiedState); + this.links = Object.freeze(copiedLinks); + } +} diff --git a/src/matrix-analysis.ts b/src/matrix-analysis.ts new file mode 100644 index 0000000..f14007b --- /dev/null +++ b/src/matrix-analysis.ts @@ -0,0 +1,83 @@ +import { SearchLimitError } from './errors.ts'; +import { readAt } from './indexed.ts'; +import type { LockModel } from './lock-model.ts'; + +interface Rational { + readonly n: bigint; + readonly d: bigint; +} + +const abs = (value: bigint): bigint => value < 0n ? -value : value; + +/** Canonical exact rational: coprime numerator/denominator, positive denominator. */ +function fraction(n: bigint, d: bigint = 1n): Rational { + if (d === 0n) throw new Error('Internal zero-denominator invariant failed.'); + if (n === 0n) return { n: 0n, d: 1n }; + if (d < 0n) { n = -n; d = -d; } + let a = abs(n); + let b = d; + while (b !== 0n) [a, b] = [b, a % b]; + return { n: n / a, d: d / a }; +} + +const divide = (a: Rational, b: Rational): Rational => fraction(a.n * b.d, a.d * b.n); +const subtractProduct = (a: Rational, b: Rational, c: Rational): Rational => + fraction(a.n * b.d * c.d - b.n * c.n * a.d, a.d * b.d * c.d); + +export type MatrixAnalysis = + | { readonly kind: 'inconsistent'; readonly rank: number } + | { readonly kind: 'singular'; readonly rank: number } + | { readonly kind: 'noninteger'; readonly rank: number } + | { readonly kind: 'unique-integer'; readonly rank: number; readonly net: readonly number[]; readonly lowerBound: number }; + +/** + * Exact necessary equation A z = goal - state. links[source][target] is + * transposed into A[target][source]; selected-pin motion gives the diagonal 1. + * Rational inconsistency or fractional unique z proves no integer path exists. + * Rank deficiency alone says nothing about reachability and requires exact BFS. + */ +export function analyzeMatrix({ state, links }: LockModel): MatrixAnalysis { + const n = state.length; + const rows = Array.from({ length: n }, (_, target) => [ + ...Array.from({ length: n }, (_, source) => fraction(BigInt(source === target ? 1 : readAt(readAt(links, source), target)))), + fraction(BigInt(4 - readAt(state, target))), + ]); + const columns: number[] = []; + let rank = 0; + for (let column = 0; column < n; column += 1) { + let pivot = rank; + while (pivot < n && readAt(readAt(rows, pivot), column).n === 0n) pivot += 1; + if (pivot === n) continue; + const pivotRow = readAt(rows, pivot); + rows[pivot] = readAt(rows, rank); + rows[rank] = pivotRow; + const divisor = readAt(pivotRow, column); + for (let j = column; j <= n; j += 1) pivotRow[j] = divide(readAt(pivotRow, j), divisor); + for (let row = 0; row < n; row += 1) { + const values = readAt(rows, row); + const factor = readAt(values, column); + if (row === rank || factor.n === 0n) continue; + for (let j = column; j <= n; j += 1) values[j] = subtractProduct(readAt(values, j), factor, readAt(pivotRow, j)); + } + columns.push(column); + rank += 1; + } + for (let row = rank; row < n; row += 1) { + if (readAt(readAt(rows, row), n).n !== 0n) return { kind: 'inconsistent', rank }; + } + if (rank < n) return { kind: 'singular', rank }; + const exact: Rational[] = Array.from({ length: n }, () => fraction(0n)); + for (let row = 0; row < n; row += 1) exact[readAt(columns, row)] = readAt(readAt(rows, row), n); + if (exact.some((value) => value.d !== 1n)) return { kind: 'noninteger', rank }; + const net = exact.map(({ n: numerator }) => { + if (abs(numerator) > BigInt(Number.MAX_SAFE_INTEGER)) { + throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, String(numerator)); + } + return Number(numerator); + }); + const lowerBound = net.reduce((sum, value) => sum + Math.ceil(Math.abs(value) / 6), 0); + if (!Number.isSafeInteger(lowerBound)) { + throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, lowerBound); + } + return { kind: 'unique-integer', rank, net, lowerBound }; +} diff --git a/src/matrix-search.ts b/src/matrix-search.ts new file mode 100644 index 0000000..beed0ce --- /dev/null +++ b/src/matrix-search.ts @@ -0,0 +1,140 @@ +import { SearchLimitError } from './errors.ts'; +import { readAt, readByte } from './indexed.ts'; +import { IndexedHeap } from './indexed-heap.ts'; +import type { SearchNode } from './indexed-heap.ts'; +import type { LockModel } from './lock-model.ts'; +import { analyzeMatrix } from './matrix-analysis.ts'; +import { BfsSearch } from './search-bfs.ts'; +import { commandFor, decodeDigits, movementLimits } from './search-limits.ts'; +import type { PreparedSearch, SearchBudget } from './search-limits.ts'; +import type { Command, SearchStrategy } from './types.ts'; + +/** + * Sufficient certificate only: legal single actions for nonzero net entries + * attain the global lower bound. Failure is discarded and full A* still runs. + */ +function greedyCertificate(prepared: PreparedSearch, net: readonly number[], budget: SearchBudget): readonly Command[] | null { + if (net.some((value) => Math.abs(value) > 6)) return null; + const remaining = [...net]; + const digits = new Uint8Array(prepared.n); + decodeDigits(prepared.source, digits); + const commands: Command[] = []; + let pending = remaining.filter(Boolean).length; + while (pending > 0) { + let selected = -1; + for (let plate = 0; plate < prepared.n; plate += 1) { + const delta = readAt(remaining, plate); + if (delta === 0) continue; + const limits = movementLimits(digits, readAt(prepared.effects, plate)); + if (Math.abs(delta) <= limits[delta > 0 ? 0 : 1]) { selected = plate; break; } + } + if (selected === -1) return null; + const delta = readAt(remaining, selected); + const effect = readAt(prepared.effects, selected); + for (const { target, sign } of effect.shifts) { + digits[target] = readByte(digits, target) + delta * sign; + } + commands.push(commandFor(selected, delta)); + remaining[selected] = 0; + pending -= 1; + } + budget.check('maxVisited', commands.length + 1); + budget.check('maxExpanded', commands.length); + return commands; +} + +function reconstruct(node: SearchNode): readonly Command[] { + const commands: Command[] = []; + while (node.previous !== null) { + commands.push(commandFor(node.plate, node.delta)); + node = node.previous; + } + return commands.reverse(); +} + +/** + * Invertible A gives a unique remaining net r for each physical state. An action + * changes one r[i] by at most six, so h = sum ceil(abs(r[i])/6) is consistent. + * Closed nodes are final and the first popped goal minimizes action count. + */ +export class MatrixSearch implements SearchStrategy { + private readonly model: LockModel; + private readonly prepared: PreparedSearch; + private readonly budget: SearchBudget; + + constructor(model: LockModel, prepared: PreparedSearch, budget: SearchBudget) { + this.model = model; + this.prepared = prepared; + this.budget = budget; + } + + solve(): readonly Command[] | null { + const { n, source, goal, effects } = this.prepared; + const budget = this.budget; + if (source === goal) { budget.visit(); return []; } + const analysis = analyzeMatrix(this.model); + if (analysis.kind === 'inconsistent' || analysis.kind === 'noninteger') return null; + if (analysis.kind === 'singular') return new BfsSearch(this.prepared, budget).solve(); + const certificate = greedyCertificate(this.prepared, analysis.net, budget); + if (certificate !== null) return certificate; + + budget.visit(); + const root: SearchNode = { + key: source, g: 0, h: analysis.lowerBound, r: analysis.net, + previous: null, plate: -1, delta: 0, sequence: 0, heapIndex: -1, closed: false, + }; + const heap = new IndexedHeap(); + heap.push(root); + const records = new Map([[source, root]]); + const digits = new Uint8Array(n); + let sequence = 1; + while (heap.size > 0) { + const node = heap.pop(); + if (node.key === goal) return reconstruct(node); + budget.expand(); + node.closed = true; + decodeDigits(node.key, digits); + for (let plate = 0; plate < n; plate += 1) { + const effect = effects[plate]; + const previousNet = node.r[plate]; + if (effect === undefined || previousNet === undefined) throw new Error('Internal search dimension invariant failed.'); + const limits = movementLimits(digits, effect); + const preferred = previousNet < 0 ? -1 : 1; + for (let side = 0; side < 2; side += 1) { + const sign = side === 0 ? preferred : -preferred; + const limit = limits[sign > 0 ? 0 : 1]; + for (let steps = limit; steps >= 1; steps -= 1) { + const delta = sign * steps; + const key = node.key + delta * effect.offset; + const g = node.g + 1; + const existing = records.get(key); + if (existing !== undefined && (existing.closed || existing.g <= g)) continue; + if (existing !== undefined) { + // The physical state uniquely determines r/h; finalized parent + // and a lower g are the only changes required by decrease-key. + existing.g = g; + existing.previous = node; + existing.plate = plate; + existing.delta = delta; + heap.up(existing); + continue; + } + budget.visit(); + budget.check('maxFrontier', heap.size + 1); + const r = [...node.r]; + const remaining = previousNet - delta; + r[plate] = remaining; + const h = node.h - Math.ceil(Math.abs(previousNet) / 6) + Math.ceil(Math.abs(remaining) / 6); + if (!Number.isSafeInteger(remaining) || !Number.isSafeInteger(g + h)) { + throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, String(remaining)); + } + const next: SearchNode = { key, g, h, r, previous: node, plate, delta, sequence: sequence++, heapIndex: -1, closed: false }; + records.set(key, next); + heap.push(next); + } + } + } + } + return null; + } +} diff --git a/src/search-bfs.ts b/src/search-bfs.ts new file mode 100644 index 0000000..c683870 --- /dev/null +++ b/src/search-bfs.ts @@ -0,0 +1,110 @@ +import { SearchLimitError } from './errors.ts'; +import { readAt, readByte, readInt32 } from './indexed.ts'; +import { commandFor, decodeDigits, movementLimits } from './search-limits.ts'; +import type { PreparedSearch, SearchBudget } from './search-limits.ts'; +import type { Command, SearchStrategy } from './types.ts'; + +interface SparseRecord { + readonly parent: number; + readonly action: number; +} + +type Records = + | { readonly dense: true; readonly parents: Int32Array; readonly actions: Uint8Array } + | { readonly dense: false; readonly parents: Map }; + +function unpack(packed: number): Command { + const plateDirection = Math.floor(packed / 6); + const sign = plateDirection % 2 === 0 ? 1 : -1; + return commandFor(Math.floor(plateDirection / 2), sign * (packed % 6 + 1)); +} + +function reconstruct(records: Records, source: number, code: number): readonly Command[] { + const commands: Command[] = []; + while (code !== source) { + if (records.dense) { + commands.push(unpack(readByte(records.actions, code))); + code = readInt32(records.parents, code); + } else { + const node = records.parents.get(code); + if (node === undefined) throw new Error('Internal BFS predecessor invariant failed.'); + commands.push(unpack(node.action)); + code = node.parent; + } + } + return commands.reverse(); +} + +/** Exact action BFS; source index, positive sign, then larger steps win ties. */ +export class BfsSearch implements SearchStrategy { + private readonly prepared: PreparedSearch; + private readonly budget: SearchBudget; + + constructor(prepared: PreparedSearch, budget: SearchBudget) { + this.prepared = prepared; + this.budget = budget; + } + + solve(): readonly Command[] | null { + const { n, size, source, goal, effects } = this.prepared; + const budget = this.budget; + budget.visit(); + if (source === goal) return []; + + // Signed parents need two sentinel values. Queue holds only the live frontier. + const capacity = Math.min(size, budget.options.maxVisited, budget.options.maxFrontier); + const denseBytes = size * 5 + capacity * 4; + const dense = size <= 0x7fffffff && denseBytes <= budget.options.maxDenseBytes; + let records: Records; + let queue: Int32Array | number[]; + try { + records = dense + ? { dense: true, parents: new Int32Array(size).fill(-2), actions: new Uint8Array(size) } + : { dense: false, parents: new Map() }; + queue = dense ? new Int32Array(capacity) : []; + } catch (error) { + if (!(error instanceof RangeError)) throw error; + throw new SearchLimitError('maxDenseBytes', budget.options.maxDenseBytes, denseBytes, { cause: error }); + } + if (records.dense) records.parents[source] = -1; + else records.parents.set(source, { parent: -1, action: -1 }); + queue[0] = source; + let head = 0; + let tail = 1 % capacity; + let frontier = 1; + const digits = new Uint8Array(n); + + while (frontier > 0) { + budget.expand(); + const code = queue instanceof Int32Array ? readInt32(queue, head) : readAt(queue, head); + head = (head + 1) % capacity; + frontier -= 1; + decodeDigits(code, digits); + for (let plate = 0; plate < n; plate += 1) { + const effect = effects[plate]; + if (effect === undefined) throw new Error('Internal search dimension invariant failed.'); + const limits = movementLimits(digits, effect); + for (let direction = 0; direction < 2; direction += 1) { + const sign = direction === 0 ? 1 : -1; + const limit = direction === 0 ? limits[0] : limits[1]; + for (let steps = limit; steps >= 1; steps -= 1) { + const next = code + sign * steps * effect.offset; + if (records.dense ? readInt32(records.parents, next) !== -2 : records.parents.has(next)) continue; + budget.visit(); + const action = (plate * 2 + direction) * 6 + steps - 1; + if (records.dense) { + records.parents[next] = code; + records.actions[next] = action; + } else records.parents.set(next, { parent: code, action }); + if (next === goal) return reconstruct(records, source, next); + budget.check('maxFrontier', frontier + 1); + queue[tail] = next; + tail = (tail + 1) % capacity; + frontier += 1; + } + } + } + } + return null; + } +} diff --git a/src/search-limits.ts b/src/search-limits.ts new file mode 100644 index 0000000..ab8fc1d --- /dev/null +++ b/src/search-limits.ts @@ -0,0 +1,123 @@ +import { SearchLimitError } from './errors.ts'; +import { readAt, readByte } from './indexed.ts'; +import type { LockModel } from './lock-model.ts'; +import type { Command, Delta } from './types.ts'; + +export interface SearchLimits { + readonly maxVisited: number; + readonly maxExpanded: number; + readonly maxFrontier: number; + readonly maxDenseBytes: number; +} + +const DEFAULTS: SearchLimits = Object.freeze({ + maxVisited: 2_000_000, + maxExpanded: 1_000_000, + maxFrontier: 1_000_000, + maxDenseBytes: 64 * 1024 * 1024, +}); + +/** Internal budgets are intentionally absent from the public two-argument API. */ +export class SearchBudget { + readonly options: SearchLimits; + private visited = 0; + private expanded = 0; + + constructor(options: Partial = {}) { + this.options = { ...DEFAULTS, ...options }; + } + + check(name: keyof SearchLimits, used: number): void { + if (used > this.options[name]) throw new SearchLimitError(name, this.options[name], used); + } + + visit(): void { + this.check('maxVisited', this.visited + 1); + this.visited += 1; + } + + expand(): void { + this.check('maxExpanded', this.expanded + 1); + this.expanded += 1; + } +} + +export interface Effect { + readonly shifts: readonly { readonly target: number; readonly sign: number }[]; + readonly offset: number; +} + +/** Base-7 encoding and precomputed direct effects belong to one search instance. */ +export class PreparedSearch { + readonly n: number; + readonly size: number; + readonly source: number; + readonly goal: number; + readonly effects: readonly Effect[]; + + constructor(model: LockModel) { + const { state, links } = model; + const n = state.length; + const size = 7 ** n; + // Check before matrix work and even the already-open shortcut. N=18 is safe. + if (!Number.isSafeInteger(size)) { + throw new SearchLimitError('stateEncoding', Number.MAX_SAFE_INTEGER, size); + } + const powers = Array.from({ length: n }, (_, index) => 7 ** (n - index - 1)); + let source = 0; + let goal = 0; + for (let index = 0; index < n; index += 1) { + const power = readAt(powers, index); + source += (readAt(state, index) - 1) * power; + goal += 3 * power; + } + this.n = n; + this.size = size; + this.source = source; + this.goal = goal; + this.effects = links.map((row, plate) => { + const shifts: { target: number; sign: number }[] = []; + let offset = 0; + for (let target = 0; target < n; target += 1) { + const sign = plate === target ? 1 : readAt(row, target); + if (sign === 0) continue; + shifts.push({ target, sign }); + offset += sign * readAt(powers, target); + } + return { shifts, offset }; + }); + } +} + +export function decodeDigits(code: number, digits: Uint8Array): void { + for (let index = digits.length - 1; index >= 0; index -= 1) { + digits[index] = code % 7; + code = Math.floor(code / 7); + } +} + +/** Monotone direct motion makes these endpoint bounds valid for every unit step. */ +export function movementLimits(digits: Uint8Array, effect: Effect): readonly [number, number] { + let plus = 6; + let minus = 6; + for (const { target, sign } of effect.shifts) { + const position = readByte(digits, target); + if (sign === 1) { + if (6 - position < plus) plus = 6 - position; + if (position < minus) minus = position; + } else { + if (position < plus) plus = position; + if (6 - position < minus) minus = 6 - position; + } + } + return [plus, minus]; +} + +function isDelta(value: number): value is Delta { + return Number.isInteger(value) && value !== 0 && value >= -6 && value <= 6; +} + +export function commandFor(plate: number, delta: number): Command { + if (!isDelta(delta)) throw new Error('Internal command displacement invariant failed.'); + return [plate, delta]; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..fe44fab --- /dev/null +++ b/src/types.ts @@ -0,0 +1,16 @@ +/** A pin's numbered position; all pins are open at position 4. */ +export type Position = 1 | 2 | 3 | 4 | 5 | 6 | 7; +/** Direct influence from links[source][target]; the diagonal must be zero. */ +export type Link = -1 | 0 | 1; +/** Signed numeric pin displacement in one action; zero is not an action. */ +export type Delta = -6 | -5 | -4 | -3 | -2 | -1 | 1 | 2 | 3 | 4 | 5 | 6; +export type State = readonly [Position, Position, ...Position[]]; +export type Command = readonly [index: number, delta: Delta]; +/** A square matrix when S has a fixed tuple length; runtime-checked otherwise. */ +export type Links = { + readonly [Row in keyof S]: { readonly [Column in keyof S]: Link }; +}; + +export interface SearchStrategy { + solve(): readonly Command[] | null; +} diff --git a/test/benchmark.test.mjs b/test/benchmark.test.mjs deleted file mode 100644 index 2b7a6c4..0000000 --- a/test/benchmark.test.mjs +++ /dev/null @@ -1,193 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import { loadCatalog, loadManifest, sha256 } from '../benchmarks/catalog.mjs'; -import { commandMetrics, groupReferenceClicks, replayCommands } from '../benchmarks/validation.mjs'; -import { - parseBenchmarkArgs, statistics, renderMarkdown, runBenchmark, - publishReport, runAndPublish, verifyReferenceSource, verifyBaseline, -} from '../benchmarks/harness.mjs'; - -const definition = { state: [3, 4], links: [[0, 0], [0, 0]] }; -const commands = [{ plate: 1, direction: 'left', steps: 1 }]; -const lock = { - id: 'test-lock', title: 'Test lock', definition, expectedActions: 1, - baselineCommands: commands, - historicalReference: { - kind: 'cached-observation', A: 1, U: 1, C: 1, plateSwitches: 0, commands, - }, -}; -const makeSolver = (solve = () => ({ status: 'solved', commands })) => ({ - label: 'Test solver', solve, normalize: (result) => result, requiresOptimality: true, -}); -const implementations = () => Object.fromEntries( - ['matrix-astar', 'bfs', 'baseline'].map((name) => [name, makeSolver()]), -); -const options = () => ({ - locks: [structuredClone(lock)], implementations: implementations(), - repetitions: 2, warmups: 1, metadata: { test: true }, clock: (() => { - let current = 0; return () => current++; - })(), -}); - -test('timing statistics sort a copy and compute even/odd medians', () => { - const samples = [9, 1, 5, 3]; - assert.deepEqual(statistics(samples), { min: 1, median: 4, max: 9 }); - assert.deepEqual(samples, [9, 1, 5, 3]); - assert.deepEqual(statistics([6]), { min: 6, median: 6, max: 6 }); - assert.deepEqual(statistics([3, 1, 2]), { min: 1, median: 2, max: 3 }); - for (const invalid of [[], [-1], [NaN], [Infinity]]) { - assert.throws(() => statistics(invalid), /sample/i); - } -}); - -test('CLI accepts reproducible settings and rejects malformed arguments', () => { - const parsed = parseBenchmarkArgs(['--repetitions', '3', '--warmups', '0', - '--output-dir', 'somewhere', '--reference', '/source', '--smoke']); - assert.equal(parsed.repetitions, 3); - assert.equal(parsed.warmups, 0); - assert.equal(parsed.outputDir, 'somewhere'); - assert.equal(parsed.reference, '/source'); - assert.equal(parsed.smoke, true); - assert.equal(parseBenchmarkArgs([]).repetitions, 5); - for (const args of [['--repetitions', '0'], ['--warmups', '-1'], - ['--repetitions', '1.5'], ['--repetitions', '01'], ['--reference'], - ['--mystery'], ['--warmups', '1', '--warmups', '2']]) { - assert.throws(() => parseBenchmarkArgs(args), /argument|integer|duplicate|missing/i); - } -}); - -test('independent replay catches blocked intermediate clicks and invalid commands', () => { - assert.deepEqual(replayCommands(definition, commands), [4, 4]); - const start = { state: [6, 6], links: [[0, 1], [0, 0]] }; - assert.throws(() => replayCommands(start, [{ plate: 1, direction: 'left', steps: 2 }]), /blocked/i); - assert.deepEqual(start.state, [6, 6]); - for (const invalid of [{ plate: 0, direction: 'left', steps: 1 }, - { plate: 1, direction: 'up', steps: 1 }, { plate: 1, direction: 'left', steps: 7 }, - { plate: 1, direction: 'left', steps: 0 }]) { - assert.throws(() => replayCommands(definition, [invalid]), /command/i); - } - assert.deepEqual(commandMetrics([ - ...commands, { plate: 2, direction: 'right', steps: 2 }, ...commands, - ]), { A: 3, U: 2, C: 4, plateSwitches: 2 }); -}); - -test('reference click grouping preserves direction changes and checks input', () => { - assert.deepEqual(groupReferenceClicks([{ plate: 0, d: 1 }, { plate: 0, d: 1 }, - { plate: 0, d: -1 }, { plate: 1, d: -1 }]), [ - { plate: 1, direction: 'left', steps: 2 }, - { plate: 1, direction: 'right', steps: 1 }, - { plate: 2, direction: 'right', steps: 1 }, - ]); - assert.equal(groupReferenceClicks(null), null); - assert.throws(() => groupReferenceClicks([{ plate: 0, d: 2 }]), /reference/i); -}); - -test('pinned catalog data and unmodified Apache baseline hashes verify', () => { - assert.equal(loadCatalog().length, 45); - assert.equal(verifyBaseline().revision, 'a87e739a22ccc4215d6d9b14fa06b0738171cb60'); - const source = loadManifest().source; - assert.throws(() => verifyReferenceSource({ revision: 'wrong', html: '' }, source), /revision/i); - assert.throws(() => verifyReferenceSource({ revision: source.revision, html: 'modified' }, source), /hash/i); - const block = '/* solver-start */ function solveLock() { return []; } /* solver-end */'; - const html = ``; - const syntheticSource = { revision: 'synthetic', solver: { - sha256: sha256(html), blockSha256: sha256(block), - startMarker: '/* solver-start', endMarker: '/* solver-end */', - } }; - assert.equal(verifyReferenceSource({ revision: 'synthetic', html }, syntheticSource), block); - assert.throws(() => verifyReferenceSource({ revision: 'synthetic', html }, { - ...syntheticSource, solver: { ...syntheticSource.solver, blockSha256: 'wrong' }, - }), /block hash/i); -}); - -test('benchmark records samples, counts, scope, and separates cached reference observations', () => { - const report = runBenchmark(options()); - assert.equal(report.correctness.passed, true); - assert.equal(report.config.lockCount, 1); - assert.equal(report.config.scope, 'subset'); - assert.match(report.config.inputSha256, /^[0-9a-f]{64}$/); - assert.equal(report.results[0].solvers['matrix-astar'].A, 1); - assert.deepEqual(report.results[0].solvers.bfs.timingMs, { min: 1, median: 1, max: 1 }); - assert.deepEqual(report.results[0].solvers.bfs.samplesMs, [1, 1]); - assert.equal(report.results[0].historicalReference.kind, 'cached-observation'); - assert.equal(report.comparisons.historicalReference.timingCompared, false); - assert.equal(report.comparisons.bfs.actions.ties, 1); - const markdown = renderMarkdown(report); - assert.match(markdown, /A = actions/); - assert.match(markdown, /cached/i); - assert.match(markdown, /Test lock/); - assert.match(markdown, /subset/i); -}); - -test('timing comparisons and speedup ratios follow measured evidence in both directions', () => { - for (const matrixElapsed of [1, 2, 4]) { - let time = 0; - const setup = options(); setup.warmups = 0; setup.clock = () => time; - for (const [name, implementation] of Object.entries(setup.implementations)) { - implementation.solve = () => { - time += name === 'matrix-astar' ? matrixElapsed : 2; - return { status: 'solved', commands }; - }; - } - const result = runBenchmark(setup).comparisons.bfs.timing; - assert.equal(result[matrixElapsed < 2 ? 'wins' : matrixElapsed > 2 ? 'losses' : 'ties'], 1); - assert.equal(result.geometricMeanSpeedup, 2 / matrixElapsed); - assert.equal(result.ratioOfSummedMedians, 2 / matrixElapsed); - } -}); - -test('benchmark validates warmups and every measured solution outside timing', () => { - for (const failAt of [1, 2, 3]) { - const setup = options(); let calls = 0; - setup.implementations['matrix-astar'] = makeSolver(() => { - calls += 1; - return calls === failAt ? { status: 'solved', commands: [] } : { status: 'solved', commands }; - }); - assert.throws(() => runBenchmark(setup), /target|minimum/i); - } -}); - -test('benchmark rejects wrong optimum, mutation, nondeterminism, and reference metric drift', () => { - const wrongMinimum = options(); wrongMinimum.locks[0].expectedActions = 2; - assert.throws(() => runBenchmark(wrongMinimum), /minimum/i); - const mutation = options(); - mutation.implementations.bfs = makeSolver((value) => { - value.links[1][0] = 1; return { status: 'solved', commands }; - }); - assert.throws(() => runBenchmark(mutation), /mutat/i); - const drift = options(); - drift.implementations.reference = { ...makeSolver(), requiresOptimality: false }; - drift.locks[0].historicalReference.C = 2; - assert.throws(() => runBenchmark(drift), /reference|historical/i); - const changedPath = options(); let calls = 0; - changedPath.implementations.reference = { ...makeSolver(() => { - calls += 1; - return { status: 'solved', commands: calls === 1 ? commands : [ - { plate: 1, direction: 'left', steps: 2 }, - { plate: 1, direction: 'right', steps: 1 }, - ] }; - }), requiresOptimality: false }; - assert.throws(() => runBenchmark(changedPath), /determin|reference/i); -}); - -test('report pair is published together; correctness failure leaves existing evidence intact', async () => { - const root = await mkdtemp(join(tmpdir(), 'lock-benchmark-')); - try { - await writeFile(join(root, 'existing.txt'), 'keep'); - const paths = await publishReport(runBenchmark(options()), root); - assert.equal(JSON.parse(await readFile(paths.jsonPath, 'utf8')).correctness.passed, true); - assert.match(await readFile(paths.markdownPath, 'utf8'), /Test lock/); - assert.deepEqual((await readdir(paths.directory)).sort(), ['benchmark.json', 'benchmark.md']); - const before = (await readdir(root)).sort(); - const bad = options(); bad.locks[0].expectedActions = 99; - await assert.rejects(runAndPublish({ ...bad, outputDir: root }), /minimum/i); - assert.deepEqual((await readdir(root)).sort(), before); - assert.equal(await readFile(join(root, 'existing.txt'), 'utf8'), 'keep'); - await assert.rejects(publishReport({ correctness: { passed: false } }, root), /correctness/i); - } finally { - await rm(root, { recursive: true, force: true }); - } -}); diff --git a/test/browser-bundle.test.mjs b/test/browser-bundle.test.mjs deleted file mode 100644 index 8a24ff1..0000000 --- a/test/browser-bundle.test.mjs +++ /dev/null @@ -1,121 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { existsSync } from 'node:fs'; -import { readFile, mkdtemp, mkdir, writeFile, rm } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { spawnSync } from 'node:child_process'; -import vm from 'node:vm'; -import { solveLock } from '../src/index.mjs'; - -const root = fileURLToPath(new URL('../', import.meta.url)); -const builderPath = join(root, 'scripts/build-browser.mjs'); -const artifactPath = join(root, 'dist/gothic-lock-solver.js'); -const independent = (state) => ({ state, links: state.map(() => state.map(() => 0)) }); -const normalize = (value) => JSON.parse(JSON.stringify(value)); - -async function builder() { - assert.ok(existsSync(builderPath), 'the reproducible classic-script builder must exist'); - return import('../scripts/build-browser.mjs'); -} - -test('ready browser artifact is reproducible and the CLI verifies source synchronization', async () => { - assert.ok(existsSync(builderPath), 'the reproducible classic-script builder must exist'); - const result = spawnSync(process.execPath, [builderPath, '--check'], { encoding: 'utf8', cwd: tmpdir() }); - assert.equal(result.status, 0, result.stderr); - const { buildBrowserBundle } = await builder(); - const first = await readFile(artifactPath, 'utf8'); - assert.equal(await buildBrowserBundle({ check: true }), first); - assert.equal(await buildBrowserBundle({ check: true }), first); - assert.match(first, /SPDX-License-Identifier: Apache-2\.0/); - assert.match(first, /Apache License[\s\S]+Version 2\.0, January 2004/); - assert.match(first, /gothic-lock-solver/); -}); - -test('classic script loads without module, DOM, Node, network, or dynamic-code facilities', async () => { - assert.ok(existsSync(artifactPath), 'a ready classic-script artifact must be checked in'); - const source = await readFile(artifactPath, 'utf8'); - const context = vm.createContext({ untouched: 123 }, { codeGeneration: { strings: false, wasm: false } }); - new vm.Script(source, { filename: 'gothic-lock-solver.js' }).runInContext(context, { timeout: 5000 }); - assert.deepEqual(Object.keys(context).sort(), ['GothicLockSolver', 'untouched']); - assert.equal(context.untouched, 123); - assert.equal(vm.runInContext('typeof process + ":" + typeof require + ":" + typeof document + ":" + typeof fetch', context), 'undefined:undefined:undefined:undefined'); - assert.deepEqual(Object.keys(context.GothicLockSolver).sort(), ['SearchLimitError', 'applyCommand', 'generateTransitions', 'solveLock']); - assert.equal(Object.isFrozen(context.GothicLockSolver), true); - const definitions = [ - independent([1, 7, 4, 2, 6]), - independent([1, 7, 2, 6, 3, 5, 4, 1]), - { state: [3, 1, 1], links: [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]] }, - { state: [1, 1, 1], links: [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]] }, - { state: [1, 1], links: [[0, 1], [1, 0]] }, - ]; - for (const definition of definitions) { - const before = structuredClone(definition); - assert.deepEqual(normalize(context.GothicLockSolver.solveLock(definition)), solveLock(definition)); - assert.deepEqual(definition, before); - } - const definition = independent([1, 7]); - assert.deepEqual(normalize(context.GothicLockSolver.solveLock(definition, { algorithm: 'bfs' })), solveLock(definition, { algorithm: 'bfs' })); - assert.throws(() => context.GothicLockSolver.solveLock(definition, { algorithm: 'bfs', maxExpanded: 0 }), - (error) => error instanceof context.GothicLockSolver.SearchLimitError && error.limit === 'maxExpanded'); - const otherContext = vm.createContext({}, { codeGeneration: { strings: false, wasm: false } }); - new vm.Script(source).runInContext(otherContext, { timeout: 5000 }); - assert.notEqual(otherContext.GothicLockSolver, context.GothicLockSolver); - assert.notEqual(otherContext.GothicLockSolver.SearchLimitError, context.GothicLockSolver.SearchLimitError); - assert.deepEqual(normalize(otherContext.GothicLockSolver.solveLock(definition)), solveLock(definition)); -}); - -test('builder rejects unsupported imports and exports before replacing an artifact', async () => { - const { buildBrowserBundle, BROWSER_MODULES } = await builder(); - const temporary = await mkdtemp(join(tmpdir(), 'gothic-browser-builder-')); - try { - await mkdir(join(temporary, 'src')); - for (const name of BROWSER_MODULES) { - await writeFile(join(temporary, name), await readFile(join(root, name))); - } - await writeFile(join(temporary, 'LICENSE'), await readFile(join(root, 'LICENSE'))); - const original = await buildBrowserBundle({ root: temporary }); - assert.equal(original, await readFile(artifactPath, 'utf8'), 'generation must not depend on the checkout path'); - const target = join(temporary, 'src/index.mjs'); - const source = await readFile(target, 'utf8'); - for (const unsupported of [ - "import ignored from './transition.mjs';", - "import { readFile } from 'node:fs';", - "import { x } from './not-allowlisted.mjs';", - "export * from './transition.mjs';", - 'export default solveLock;', - "const lazy = () => import('./transition.mjs');", - 'const address = import.meta.url;', - ]) { - await writeFile(target, `${source}\n${unsupported}\n`); - await assert.rejects(() => buildBrowserBundle({ root: temporary }), /Unsupported|unsupported|allowlist/); - assert.equal(await readFile(join(temporary, 'dist/gothic-lock-solver.js'), 'utf8'), original); - } - } finally { - await rm(temporary, { recursive: true, force: true }); - } -}); - -test('check mode fails on stale source or changed artifact without writing', async () => { - const { buildBrowserBundle, BROWSER_MODULES } = await builder(); - const temporary = await mkdtemp(join(tmpdir(), 'gothic-browser-sync-')); - try { - await mkdir(join(temporary, 'src')); - for (const name of BROWSER_MODULES) await writeFile(join(temporary, name), await readFile(join(root, name))); - await writeFile(join(temporary, 'LICENSE'), await readFile(join(root, 'LICENSE'))); - await buildBrowserBundle({ root: temporary }); - const artifact = join(temporary, 'dist/gothic-lock-solver.js'); - const original = await readFile(artifact, 'utf8'); - const source = join(temporary, 'src/index.mjs'); - await writeFile(source, `${await readFile(source, 'utf8')}\n// source synchronization fixture\n`); - await assert.rejects(() => buildBrowserBundle({ root: temporary, check: true }), /out of date/); - assert.equal(await readFile(artifact, 'utf8'), original); - await buildBrowserBundle({ root: temporary }); - await writeFile(artifact, 'stale artifact'); - await assert.rejects(() => buildBrowserBundle({ root: temporary, check: true }), /out of date/); - assert.equal(await readFile(artifact, 'utf8'), 'stale artifact'); - } finally { - await rm(temporary, { recursive: true, force: true }); - } -}); diff --git a/test/browser-smoke.html b/test/browser-smoke.html deleted file mode 100644 index e0bcc88..0000000 --- a/test/browser-smoke.html +++ /dev/null @@ -1,40 +0,0 @@ - - - -Gothic solver browser verification -

Browser ESM and Worker verification

-
Running…
- - diff --git a/test/catalog.test.mjs b/test/catalog.test.mjs deleted file mode 100644 index 9544f81..0000000 --- a/test/catalog.test.mjs +++ /dev/null @@ -1,45 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { solveLock } from '../src/index.mjs'; -import { loadCatalog, loadManifest } from '../benchmarks/catalog.mjs'; -import { commandMetrics, replayCommands } from '../benchmarks/validation.mjs'; - -const catalog = loadCatalog(); - -test('catalog preserves 45 named inputs and pinned source provenance', () => { - assert.equal(catalog.length, 45); - assert.equal(new Set(catalog.map((lock) => lock.id)).size, 45); - assert.equal(loadManifest().source.revision, 'eee0bf50ebcb7fffd2b47954fd76bb015854e365'); - for (const lock of catalog) { - assert.ok(lock.code && lock.title && lock.url && lock.provenance.catalog); - assert.equal(lock.provenance.commit, loadManifest().source.revision); - assert.ok(Number.isInteger(lock.expectedActions)); - assert.equal(lock.historicalReference.kind, 'cached-observation'); - const historic = commandMetrics(lock.historicalReference.commands); - assert.equal(historic.A, lock.historicalReference.A, lock.id); - assert.equal(historic.U, lock.historicalReference.U, lock.id); - assert.equal(historic.C, lock.historicalReference.C, lock.id); - assert.deepEqual(replayCommands(lock.definition, lock.historicalReference.commands), - lock.definition.state.map(() => 4), lock.id); - } -}); - -for (const lock of catalog) { - test(`catalog ${lock.id}: default and exact BFS agree on minimum actions`, () => { - const before = structuredClone(lock.definition); - const matrix = solveLock(lock.definition); - const bfs = solveLock(lock.definition, { algorithm: 'bfs' }); - assert.equal(matrix.status, 'solved', lock.id); - assert.equal(bfs.status, 'solved', lock.id); - assert.equal(matrix.commands.length, lock.expectedActions, lock.id); - assert.equal(bfs.commands.length, lock.expectedActions, lock.id); - assert.deepEqual(bfs.commands, lock.baselineCommands, `BFS ordering: ${lock.id}`); - for (const result of [matrix, bfs]) { - assert.deepEqual(replayCommands(lock.definition, result.commands), - lock.definition.state.map(() => 4), lock.id); - assert.equal(result.metrics.commands, commandMetrics(result.commands).A); - assert.equal(result.metrics.divisions, commandMetrics(result.commands).C); - } - assert.deepEqual(lock.definition, before, `input mutation: ${lock.id}`); - }); -} diff --git a/test/cli.test.mjs b/test/cli.test.mjs deleted file mode 100644 index e9f14a4..0000000 --- a/test/cli.test.mjs +++ /dev/null @@ -1,160 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { spawnSync } from 'node:child_process'; -import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const projectRoot = dirname(dirname(fileURLToPath(import.meta.url))); -const entry = join(projectRoot, 'solve-lock.mjs'); - -async function createCase(t, definition) { - const directory = await mkdtemp(join(tmpdir(), 'gothic-lock-solver-')); - t.after(() => rm(directory, { recursive: true, force: true })); - const inputPath = join(directory, 'lock.json'); - await writeFile(inputPath, JSON.stringify(definition), 'utf8'); - return { directory, inputPath }; -} - -function run(args) { - return spawnSync(process.execPath, [entry, ...args], { encoding: 'utf8' }); -} - -const expectedSolutionJson = `{ - "status": "solved", - "initialState": [ - 1, - 7 - ], - "targetState": [ - 4, - 4 - ], - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "finalState": [ - 4, - 4 - ], - "metrics": { - "commands": 2, - "divisions": 6, - "plateSwitches": 1 - } -} -`; - -test('prints a solution without creating an output file by default', async (t) => { - const { directory, inputPath } = await createCase(t, { - state: [1, 7], - links: [[0, 0], [0, 0]], - }); - const outputPath = join(directory, 'solution.json'); - const result = run([inputPath]); - assert.equal(result.status, 0); - assert.match(result.stdout, /Найдено команд: 2/); - assert.equal(result.stderr, ''); - await assert.rejects(readFile(outputPath, 'utf8'), { code: 'ENOENT' }); -}); - -test('--output creates and then overwrites exact pretty JSON bytes', async (t) => { - const { directory, inputPath } = await createCase(t, { - state: [1, 7], - links: [[0, 0], [0, 0]], - }); - const outputPath = join(directory, 'solution.json'); - const created = run([inputPath, '--output', outputPath]); - assert.equal(created.status, 0); - assert.equal(created.stderr, ''); - assert.equal(await readFile(outputPath, 'utf8'), expectedSolutionJson); - - await writeFile(outputPath, 'stale'.repeat(1_000), 'utf8'); - const overwritten = run([inputPath, '--output', outputPath]); - assert.equal(overwritten.status, 0); - assert.match(overwritten.stdout, /Пластина 1 - влево x3/); - assert.equal(overwritten.stderr, ''); - assert.equal(await readFile(outputPath, 'utf8'), expectedSolutionJson); -}); - -test('invalid JSON exits with code 1 and a Russian error', async (t) => { - const { inputPath } = await createCase(t, { - state: [4, 4], - links: [[0, 0], [0, 0]], - }); - await writeFile(inputPath, '{', 'utf8'); - const result = run([inputPath]); - assert.equal(result.status, 1); - assert.equal(result.stdout, ''); - assert.match(result.stderr, /Ошибка: Некорректный JSON/); -}); - -test('an unsolvable lock writes JSON and exits with code 2', async (t) => { - const { directory, inputPath } = await createCase(t, { - state: [1, 7], - links: [[0, 1], [1, 0]], - }); - const outputPath = join(directory, 'solution.json'); - const result = run([inputPath, '--output', outputPath]); - assert.equal(result.status, 2); - assert.match(result.stdout, /Решение не найдено/); - assert.equal(result.stderr, ''); - assert.equal(JSON.parse(await readFile(outputPath, 'utf8')).status, 'unsolvable'); -}); - -test('read errors exit with code 1 and keep stdout empty', async (t) => { - const { directory, inputPath } = await createCase(t, { - state: [4, 4], - links: [[0, 0], [0, 0]], - }); - - const missingInput = run([join(directory, 'missing.json')]); - assert.equal(missingInput.status, 1); - assert.equal(missingInput.stdout, ''); - assert.match(missingInput.stderr, /Ошибка:/); -}); - -test('strict CLI grammar errors exit with code 1 and keep stdout empty', async (t) => { - const { inputPath, directory } = await createCase(t, { - state: [4, 4], - links: [[0, 0], [0, 0]], - }); - - for (const args of [ - [], - ['--unknown'], - ['--output'], - [''], - [inputPath, '--unknown'], - [inputPath, '--output'], - [inputPath, '--output', ''], - [inputPath, 'extra'], - [inputPath, '--output', join(directory, 'solution.json'), 'extra'], - ]) { - const invalidArgs = run(args); - assert.equal(invalidArgs.status, 1); - assert.equal(invalidArgs.stdout, ''); - assert.match(invalidArgs.stderr, /Ошибка: Использование:/); - } -}); - -test('write errors exit with code 1 and keep stdout empty', async (t) => { - const { directory, inputPath } = await createCase(t, { - state: [4, 4], - links: [[0, 0], [0, 0]], - }); - const writeError = run([inputPath, '--output', directory]); - assert.equal(writeError.status, 1); - assert.equal(writeError.stdout, ''); - assert.match(writeError.stderr, /Ошибка:/); -}); diff --git a/test/differential.test.mjs b/test/differential.test.mjs deleted file mode 100644 index 60ace08..0000000 --- a/test/differential.test.mjs +++ /dev/null @@ -1,50 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { createTargetState } from '../src/state-codec.mjs'; -import { findShortestCommands } from '../src/solver.mjs'; -import { generateTransitions } from '../src/transition.mjs'; - -function referenceBfs(definition) { - const targetKey = createTargetState(definition.state.length).join(','); - const startKey = definition.state.join(','); - if (startKey === targetKey) { - return []; - } - - const queue = [{ state: definition.state, commands: [] }]; - const visited = new Set([startKey]); - for (let head = 0; head < queue.length; head += 1) { - const current = queue[head]; - for (const transition of generateTransitions(current.state, definition.links)) { - const key = transition.state.join(','); - if (visited.has(key)) { - continue; - } - const commands = [...current.commands, transition.command]; - if (key === targetKey) { - return commands; - } - visited.add(key); - queue.push({ state: transition.state, commands }); - } - } - return null; -} - -test('typed-array BFS matches reference BFS for every N=2 lock', () => { - for (const link12 of [-1, 0, 1]) { - for (const link21 of [-1, 0, 1]) { - const links = [[0, link12], [link21, 0]]; - for (let first = 1; first <= 7; first += 1) { - for (let second = 1; second <= 7; second += 1) { - const definition = { state: [first, second], links }; - assert.deepEqual( - findShortestCommands(definition), - referenceBfs(definition), - `state=${first},${second}; links=${link12},${link21}`, - ); - } - } - } - } -}); diff --git a/test/e2e.test.mjs b/test/e2e.test.mjs deleted file mode 100644 index e0af50d..0000000 --- a/test/e2e.test.mjs +++ /dev/null @@ -1,24 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { spawnSync } from 'node:child_process'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const projectRoot = dirname(dirname(fileURLToPath(import.meta.url))); - -test('example lock produces the documented four-command solution', () => { - const result = spawnSync( - process.execPath, - [join(projectRoot, 'solve-lock.mjs'), join(projectRoot, 'examples/lock.example.json')], - { encoding: 'utf8' }, - ); - assert.equal(result.status, 0); - assert.equal( - result.stdout, - 'Найдено команд: 4\n' - + '1. Пластина 1 - влево x3\n' - + '2. Пластина 2 - вправо x3\n' - + '3. Пластина 4 - влево x2\n' - + '4. Пластина 5 - вправо x2\n', - ); -}); diff --git a/test/lock-definition.test.mjs b/test/lock-definition.test.mjs deleted file mode 100644 index c364797..0000000 --- a/test/lock-definition.test.mjs +++ /dev/null @@ -1,62 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { - LockInputError, - parseLockDefinition, - validateLockDefinition, -} from '../src/lock-definition.mjs'; - -const validLock = { - state: [7, 4, 1], - links: [ - [0, 1, -1], - [0, 0, 0], - [1, 0, 0], - ], -}; - -test('validateLockDefinition accepts and copies a valid directed lock', () => { - const result = validateLockDefinition(validLock); - assert.deepEqual(result, validLock); - assert.notEqual(result.state, validLock.state); - assert.notEqual(result.links, validLock.links); - for (const index of validLock.links.keys()) { - assert.notEqual(result.links[index], validLock.links[index]); - } - assert.notEqual(result.links[0], validLock.links[0]); -}); - -test('validateLockDefinition ignores additional input properties', () => { - const result = validateLockDefinition({ ...validLock, name: 'ignored', metadata: { source: 'test' } }); - assert.deepEqual(result, validLock); - assert.equal('name' in result, false); - assert.equal('metadata' in result, false); -}); - -for (const [name, value, message] of [ - ['requires at least two plates', { state: [4], links: [[0]] }, /не менее 2/], - ['rejects a position outside 1..7', { ...validLock, state: [7, 8, 1] }, /state\[1\]/], - ['requires a square matrix', { ...validLock, links: [[0], [0], [0]] }, /links\[0\]/], - ['rejects an unknown coefficient', { ...validLock, links: [[0, 2, 0], [0, 0, 0], [0, 0, 0]] }, /-1, 0 или 1/], - ['requires a zero diagonal', { ...validLock, links: [[1, 0, 0], [0, 0, 0], [0, 0, 0]] }, /диагонали/], -]) { - test(name, () => { - assert.throws(() => validateLockDefinition(value), message); - }); -} - -test('rejects sparse state arrays, matrix rows, and matrix entries', () => { - const cases = [ - { state: [4, , 4], links: validLock.links }, - { state: validLock.state, links: [validLock.links[0], , validLock.links[2]] }, - { state: validLock.state, links: [[0, , -1], validLock.links[1], validLock.links[2]] }, - ]; - for (const definition of cases) assert.throws(() => validateLockDefinition(definition), LockInputError); -}); - -test('parseLockDefinition reports malformed JSON as LockInputError', () => { - assert.throws( - () => parseLockDefinition('{'), - (error) => error instanceof LockInputError && /Некорректный JSON/.test(error.message), - ); -}); diff --git a/test/matrix-search.test.mjs b/test/matrix-search.test.mjs deleted file mode 100644 index 1d10a47..0000000 --- a/test/matrix-search.test.mjs +++ /dev/null @@ -1,197 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import * as api from '../src/index.mjs'; -import { findShortestCommands } from '../src/solver.mjs'; -import { validateLockDefinition } from '../src/lock-definition.mjs'; - -const independent = (state) => ({ state, links: state.map(() => state.map(() => 0)) }); -const inverseLinks = [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]]; - -// This oracle intentionally imports no production transition/encoding helpers. -function unitMove(state, links, plate, direction) { - const next = [...state]; - next[plate] += direction; - for (let target = 0; target < state.length; target += 1) { - if (target !== plate) next[target] += direction * links[plate][target]; - } - return next.some((position) => position < 1 || position > 7) ? null : next; -} - -function replay(definition, commands) { - let state = [...definition.state]; - for (const command of commands) { - assert.ok(Number.isInteger(command.plate) && command.plate >= 1 && command.plate <= state.length); - assert.ok(command.direction === 'left' || command.direction === 'right'); - assert.ok(Number.isInteger(command.steps) && command.steps >= 1 && command.steps <= 6); - for (let click = 0; click < command.steps; click += 1) { - state = unitMove(state, definition.links, command.plate - 1, command.direction === 'left' ? 1 : -1); - assert.notEqual(state, null, 'each individual division must be legal'); - } - } - assert.deepEqual(state, state.map(() => 4)); -} - -function referenceActions(definition) { - const queue = [{ state: [...definition.state], depth: 0 }]; - const visited = new Set([definition.state.join(',')]); - for (let head = 0; head < queue.length; head += 1) { - const { state, depth } = queue[head]; - if (state.every((position) => position === 4)) return depth; - for (let plate = 0; plate < state.length; plate += 1) { - for (const direction of [1, -1]) { - let next = state; - for (let steps = 1; steps <= 6; steps += 1) { - next = unitMove(next, definition.links, plate, direction); - if (next === null) break; - const key = next.join(','); - if (visited.has(key)) continue; - visited.add(key); - queue.push({ state: next, depth: depth + 1 }); - } - } - } - } - return null; -} - -test('default matrix solver accepts eight independent plates and keeps result shape', () => { - const definition = independent([1, 7, 2, 6, 3, 5, 4, 1]); - const before = structuredClone(definition); - const result = api.solveLock(definition); - assert.equal(result.status, 'solved'); - assert.equal(result.metrics.commands, 7); - assert.deepEqual(Object.keys(result), ['status', 'initialState', 'targetState', 'commands', 'finalState', 'metrics']); - replay(definition, result.commands); - assert.deepEqual(definition, before); -}); - -test('explicit BFS keeps previous deterministic command ordering', () => { - const definition = { state: [2, 7, 1], links: [[0, 0, 0], [-1, 0, -1], [-1, -1, 0]] }; - assert.deepEqual(api.solveLock(definition, { algorithm: 'bfs' }).commands, [ - { plate: 1, direction: 'left', steps: 5 }, - { plate: 3, direction: 'left', steps: 3 }, - ]); - assert.deepEqual(api.solveLock(definition, { algorithm: 'bfs' }).commands, findShortestCommands(definition)); -}); - -test('rejects unknown algorithms and invalid resource budgets even on an open lock', () => { - const definition = independent([4, 4]); - assert.throws(() => api.solveLock(definition, { algorithm: 'greedy' }), /algorithm/); - for (const value of [null, [], 'bfs']) assert.throws(() => api.solveLock(definition, value), TypeError); - for (const name of ['maxExpanded', 'maxVisited', 'maxFrontier', 'maxDenseBytes']) { - for (const value of [-1, NaN, Infinity, 0.5, Number.MAX_SAFE_INTEGER + 1, '20']) { - assert.throws(() => api.solveLock(definition, { [name]: value }), TypeError, `${name}=${value}`); - } - } -}); - -test('a safe model size is separate from safe computational representation', () => { - const definition = independent(Array(19).fill(4)); - assert.deepEqual(validateLockDefinition(definition), definition); - for (const algorithm of ['matrix-astar', 'bfs']) { - assert.throws(() => api.solveLock(definition, { algorithm }), (error) => error.name === 'SearchLimitError' && error.limit === 'stateEncoding'); - } -}); - -test('matrix equation is necessary but a legal schedule remains required', () => { - assert.equal(api.solveLock({ state: [1, 1, 1], links: inverseLinks }).status, 'unsolvable'); - const definition = { state: [3, 1, 1], links: inverseLinks }; - const result = api.solveLock(definition); - assert.equal(result.metrics.commands, 4, 'three nonzero net movements require a repeated plate here'); - assert.ok(new Set(result.commands.map((command) => command.plate)).size < result.commands.length); - replay(definition, result.commands); - assert.deepEqual(api.solveLock(definition), result, 'matrix tie selection is deterministic'); -}); - -test('fractional and inconsistent equations prove unreachable targets', () => { - for (const links of [[[0, 1], [-1, 0]], [[0, 1], [1, 0]]]) { - assert.equal(api.solveLock({ state: [3, 4], links }).status, 'unsolvable'); - } -}); - -test('singular matrices fall back to exact action BFS', () => { - const definition = { state: [1, 1], links: [[0, 1], [1, 0]] }; - assert.deepEqual(api.solveLock(definition).commands, [{ plate: 1, direction: 'left', steps: 3 }]); -}); - -test('every graph-search route throws SearchLimitError rather than unsolvable on exhaustion', () => { - assert.equal(typeof api.SearchLimitError, 'function'); - for (const [definition, algorithm] of [ - [independent([1, 7]), 'bfs'], - [{ state: [3, 1, 1], links: inverseLinks }, 'matrix-astar'], - [{ state: [1, 1], links: [[0, 1], [1, 0]] }, 'matrix-astar'], - ]) { - for (const [limit, value] of [['maxExpanded', 0], ['maxVisited', 1], ['maxFrontier', 1]]) { - assert.throws(() => api.solveLock(definition, { algorithm, [limit]: value }), - (error) => error instanceof api.SearchLimitError && error.limit === limit, - `${algorithm} ${limit}`); - } - } - assert.throws(() => findShortestCommands(independent([1, 7]), { maxExpanded: 0 }), (error) => error.name === 'SearchLimitError'); -}); - -test('zero dense-memory budget selects sparse BFS without changing the answer', () => { - const definition = { state: [3, 2, 1], links: [[0, 0, -1], [-1, 0, -1], [-1, -1, 0]] }; - assert.deepEqual(api.solveLock(definition, { algorithm: 'bfs', maxDenseBytes: 0 }), api.solveLock(definition, { algorithm: 'bfs' })); -}); - -test('greedy certificates respect visited and expanded budgets', () => { - const definition = independent([1, 7]); - for (const [limit, value] of [['maxExpanded', 1], ['maxVisited', 2]]) { - assert.throws(() => api.solveLock(definition, { [limit]: value }), (error) => error instanceof api.SearchLimitError && error.limit === limit); - } - assert.equal(api.solveLock(definition, { maxExpanded: 2, maxVisited: 3, maxFrontier: 1 }).metrics.commands, 2); - assert.equal(api.solveLock(independent([4, 4]), { maxExpanded: 0, maxVisited: 1 }).metrics.commands, 0); -}); - -test('safe state codes beyond 32 bits survive sparse BFS and matrix search', () => { - const definition = independent([1, ...Array(17).fill(4)]); - for (const algorithm of ['bfs', 'matrix-astar']) { - const result = api.solveLock(definition, { algorithm, maxDenseBytes: 0, maxVisited: 5, maxExpanded: 1, maxFrontier: 4 }); - assert.deepEqual(result.commands, [{ plate: 1, direction: 'left', steps: 3 }]); - replay(definition, result.commands); - } -}); - -test('A* handles net movement larger than one command without overestimating actions', () => { - // A z = [-3,-3,3] has unique integer z=[-3,-6,12], so at least four - // commands are necessary even though only three plates must be selected. - const definition = { state: [7, 7, 1], links: [[0, -1, 1], [0, 0, 1], [0, 0, 0]] }; - const result = api.solveLock(definition); - assert.equal(referenceActions(definition), 4); - assert.equal(result.metrics.commands, 4); - replay(definition, result.commands); -}); - -test('new matrix solver matches independent complete search for all 441 two-plate cases', () => { - for (const firstLink of [-1, 0, 1]) { - for (const secondLink of [-1, 0, 1]) { - for (let first = 1; first <= 7; first += 1) { - for (let second = 1; second <= 7; second += 1) { - const definition = { state: [first, second], links: [[0, firstLink], [secondLink, 0]] }; - const expected = referenceActions(definition); - const actual = api.solveLock(definition); - assert.equal(actual.status === 'solved' ? actual.commands.length : null, expected, JSON.stringify(definition)); - if (expected !== null) replay(definition, actual.commands); - } - } - } - } -}); - -test('matrix and both BFS storage modes match independent complete search on 192 seeded N3 inputs', () => { - let seed = 0x51405; - const random = (max) => { seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0; return seed % max; }; - for (let sample = 0; sample < 192; sample += 1) { - const definition = { - state: Array.from({ length: 3 }, () => 1 + random(7)), - links: Array.from({ length: 3 }, (_, row) => Array.from({ length: 3 }, (_, column) => row === column ? 0 : random(3) - 1)), - }; - const expected = referenceActions(definition); - for (const options of [{}, { algorithm: 'bfs' }, { algorithm: 'bfs', maxDenseBytes: 0 }]) { - const actual = api.solveLock(definition, options); - assert.equal(actual.status === 'solved' ? actual.commands.length : null, expected, JSON.stringify({ definition, options })); - if (expected !== null) replay(definition, actual.commands); - } - } -}); diff --git a/test/result.test.mjs b/test/result.test.mjs deleted file mode 100644 index fe4e4e4..0000000 --- a/test/result.test.mjs +++ /dev/null @@ -1,54 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { solveLock } from '../src/index.mjs'; -import { createSolveResult, formatConsoleResult } from '../src/result.mjs'; - -const definition = { - state: [1, 7], - links: [[0, 0], [0, 0]], -}; -const commands = [ - { plate: 1, direction: 'left', steps: 3 }, - { plate: 2, direction: 'right', steps: 3 }, -]; - -test('createSolveResult replays commands and calculates metrics', () => { - const result = createSolveResult(definition, commands); - assert.deepEqual(result.finalState, [4, 4]); - assert.deepEqual(result.metrics, { - commands: 2, - divisions: 6, - plateSwitches: 1, - }); -}); - -test('formatConsoleResult renders grouped Russian commands', () => { - const result = createSolveResult(definition, commands); - assert.equal( - formatConsoleResult(result), - 'Найдено команд: 2\n' - + '1. Пластина 1 - влево x3\n' - + '2. Пластина 2 - вправо x3', - ); -}); - -test('already-open and unsolvable results have distinct text', () => { - const open = createSolveResult({ state: [4, 4], links: definition.links }, []); - const unsolvable = createSolveResult(definition, null); - assert.equal(formatConsoleResult(open), 'Замок уже открыт.'); - assert.equal(formatConsoleResult(unsolvable), 'Решение не найдено.'); - assert.deepEqual(unsolvable.commands, []); -}); - -test('already-open result exposes independent initial and final states', () => { - const result = createSolveResult({ state: [4, 4], links: definition.links }, []); - assert.notStrictEqual(result.initialState, result.finalState); - result.finalState[0] = 1; - assert.deepEqual(result.initialState, [4, 4]); -}); - -test('solveLock validates and solves through the website-safe facade', () => { - const result = solveLock(definition); - assert.equal(result.status, 'solved'); - assert.deepEqual(result.finalState, [4, 4]); -}); diff --git a/test/solver.test.mjs b/test/solver.test.mjs deleted file mode 100644 index 810968d..0000000 --- a/test/solver.test.mjs +++ /dev/null @@ -1,85 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { - findShortestCommands, - packCommand, - unpackCommand, -} from '../src/solver.mjs'; - -const noLinks = [[0, 0], [0, 0]]; - -test('packs every possible command into one byte and restores it', () => { - for (let plate = 1; plate <= 7; plate += 1) { - for (const direction of ['left', 'right']) { - for (let steps = 1; steps <= 6; steps += 1) { - const command = { plate, direction, steps }; - const packed = packCommand(command); - assert.ok(packed >= 0 && packed <= 83); - assert.deepEqual(unpackCommand(packed), command); - } - } - } -}); - -test('returns no commands for an already open lock', () => { - assert.deepEqual( - findShortestCommands({ state: [4, 4], links: noLinks }), - [], - ); -}); - -test('uses one macro command per independent plate', () => { - assert.deepEqual( - findShortestCommands({ state: [1, 7], links: noLinks }), - [ - { plate: 1, direction: 'left', steps: 3 }, - { plate: 2, direction: 'right', steps: 3 }, - ], - ); -}); - -test('prefers the lower plate when two one-command solutions exist', () => { - const links = [[0, 1], [1, 0]]; - assert.deepEqual( - findShortestCommands({ state: [1, 1], links }), - [{ plate: 1, direction: 'left', steps: 3 }], - ); -}); - -test('returns null when every possible move is blocked', () => { - const links = [[0, 1], [1, 0]]; - assert.equal(findShortestCommands({ state: [1, 7], links }), null); -}); - -test('prefers left over right when both start equally short solutions', () => { - const definition = { - state: [2, 7, 1], - links: [ - [0, 0, 0], - [-1, 0, -1], - [-1, -1, 0], - ], - }; - assert.deepEqual(findShortestCommands(definition), [ - { plate: 1, direction: 'left', steps: 5 }, - { plate: 3, direction: 'left', steps: 3 }, - ]); -}); - -test('prefers larger steps when earlier action fields are equal', () => { - const definition = { - state: [3, 2, 1], - links: [ - [0, 0, -1], - [-1, 0, -1], - [-1, -1, 0], - ], - }; - assert.deepEqual(findShortestCommands(definition), [ - { plate: 1, direction: 'right', steps: 2 }, - { plate: 3, direction: 'right', steps: 2 }, - { plate: 2, direction: 'right', steps: 2 }, - { plate: 1, direction: 'right', steps: 3 }, - { plate: 3, direction: 'right', steps: 2 }, - ]); -}); diff --git a/test/state-codec.test.mjs b/test/state-codec.test.mjs deleted file mode 100644 index f898793..0000000 --- a/test/state-codec.test.mjs +++ /dev/null @@ -1,23 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { - createTargetState, - decodeState, - encodeState, - stateSpaceSize, -} from '../src/state-codec.mjs'; - -test('base-7 encoding has stable boundary values', () => { - assert.equal(encodeState([1, 1]), 0); - assert.equal(encodeState([7, 7]), 48); - assert.equal(stateSpaceSize(7), 823_543); -}); - -test('encodeState and decodeState round-trip a seven-plate state', () => { - const state = [7, 4, 1, 3, 6, 2, 5]; - assert.deepEqual(decodeState(encodeState(state), state.length), state); -}); - -test('createTargetState creates only center positions', () => { - assert.deepEqual(createTargetState(5), [4, 4, 4, 4, 4]); -}); diff --git a/test/transition.test.mjs b/test/transition.test.mjs deleted file mode 100644 index 2e73728..0000000 --- a/test/transition.test.mjs +++ /dev/null @@ -1,64 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { applyCommand, generateTransitions } from '../src/transition.mjs'; - -test('applyCommand moves direct and inverse links by the full step count', () => { - const state = [3, 5, 4]; - const links = [ - [0, 1, -1], - [0, 0, 0], - [0, 0, 0], - ]; - const result = applyCommand(state, links, { plate: 1, direction: 'left', steps: 2 }); - assert.deepEqual(result, [5, 7, 2]); - assert.deepEqual(state, [3, 5, 4]); -}); - -test('applyCommand does not cascade through a moved target row', () => { - const links = [ - [0, 1, 0], - [0, 0, 1], - [0, 0, 0], - ]; - assert.deepEqual( - applyCommand([3, 5, 4], links, { plate: 1, direction: 'left', steps: 2 }), - [5, 7, 4], - ); -}); - -test('applyCommand atomically blocks when one linked plate cannot finish', () => { - const state = [3, 6]; - const links = [[0, 1], [0, 0]]; - assert.deepEqual( - applyCommand(state, links, { plate: 1, direction: 'left', steps: 1 }), - [4, 7], - ); - assert.equal( - applyCommand(state, links, { plate: 1, direction: 'left', steps: 2 }), - null, - ); - assert.deepEqual(state, [3, 6]); -}); - -test('a directed source row does not imply a reverse link', () => { - const links = [[0, 1], [0, 0]]; - assert.deepEqual( - applyCommand([3, 5], links, { plate: 2, direction: 'right', steps: 1 }), - [3, 4], - ); -}); - -test('generateTransitions uses plate, direction, descending steps order', () => { - const transitions = generateTransitions([4, 4], [[0, 0], [0, 0]]); - assert.deepEqual( - transitions.slice(0, 6).map(({ command }) => command), - [ - { plate: 1, direction: 'left', steps: 3 }, - { plate: 1, direction: 'left', steps: 2 }, - { plate: 1, direction: 'left', steps: 1 }, - { plate: 1, direction: 'right', steps: 3 }, - { plate: 1, direction: 'right', steps: 2 }, - { plate: 1, direction: 'right', steps: 1 }, - ], - ); -}); diff --git a/test/web-core.test.mjs b/test/web-core.test.mjs deleted file mode 100644 index 1dcca2a..0000000 --- a/test/web-core.test.mjs +++ /dev/null @@ -1,12 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { execFileSync } from 'node:child_process'; -import { fileURLToPath } from 'node:url'; - -test('public core imports and solves in an ESM context without Node globals', () => { - const output = execFileSync(process.execPath, [ - '--experimental-vm-modules', - fileURLToPath(new URL('../scripts/verify-web-core.mjs', import.meta.url)), - ], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); - assert.equal(JSON.parse(output).status, 'passed'); -}); diff --git a/tests/browser/catalog.test.ts b/tests/browser/catalog.test.ts new file mode 100644 index 0000000..ed00a87 --- /dev/null +++ b/tests/browser/catalog.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, test } from 'vitest'; +import rawCatalog from '../../benchmarks/fixtures/catalog.json' with { type: 'json' }; +import { replayCommands } from '../../benchmarks/validation.ts'; +import { solveLock, type Link, type Position } from '../../src/index.ts'; + +function position(value: number): Position { + if (value === 1 || value === 2 || value === 3 || value === 4 || value === 5 || value === 6 || value === 7) return value; + throw new Error('Invalid pinned position'); +} +function link(value: number): Link { + if (value === -1 || value === 0 || value === 1) return value; + throw new Error('Invalid pinned link'); +} + +describe('shared immutable 45-lock catalog', () => { + test('contains exactly 45 unique original fixtures', () => { + expect(rawCatalog).toHaveLength(45); + expect(new Set(rawCatalog.map(({ id }) => id)).size).toBe(45); + }); + for (const fixture of rawCatalog) { + test(`${fixture.id}: independently replays the exact action minimum`, () => { + const state = fixture.definition.state.map(position); + const links = fixture.definition.links.map((row) => row.map(link)); + const before = JSON.stringify([state, links]); + const commands = solveLock(state, links); + expect(commands).not.toBeNull(); + expect(commands).toHaveLength(fixture.expectedActions); + expect(replayCommands(fixture.definition, commands)).toEqual(state.map(() => 4)); + expect(JSON.stringify([state, links])).toBe(before); + expect(solveLock(state, links)).toEqual(commands); + }); + } +}); diff --git a/tests/distribution/artifacts.test.ts b/tests/distribution/artifacts.test.ts new file mode 100644 index 0000000..014b439 --- /dev/null +++ b/tests/distribution/artifacts.test.ts @@ -0,0 +1,58 @@ +import { createHash } from 'node:crypto'; +import { execFileSync } from 'node:child_process'; +import { readFile, readdir } from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, test } from 'vitest'; + +const dist = new URL('../../dist/', import.meta.url); +const runtime = ['gothic-lock-solver.js', 'gothic-lock-solver.min.js', 'gothic-lock-solver.min.mjs', 'gothic-lock-solver.mjs']; + +describe('published artifacts', () => { + test('ships exactly four core runtime files and one Node CLI', async () => { + expect((await readdir(dist)).filter((name) => /\.(?:m?js)$/.test(name)).sort()).toEqual(['gothic-lock-solver.cli.mjs', ...runtime].sort()); + }); + + for (const extension of ['js', 'mjs']) { + test(`minifies ${extension} bytes and whitespace`, async () => { + const readable = await readFile(new URL(`gothic-lock-solver.${extension}`, dist), 'utf8'); + const minified = await readFile(new URL(`gothic-lock-solver.min.${extension}`, dist), 'utf8'); + expect(minified.length).toBeLessThan(readable.length * 0.8); + expect(minified.split('\n').length).toBeLessThan(readable.split('\n').length * 0.1); + expect(minified).not.toBe(readable); + }); + } + + for (const filename of runtime) { + test(`${filename} has no external loading or Node runtime requirements`, async () => { + const code = await readFile(new URL(filename, dist), 'utf8'); + expect(code).not.toMatch(/\b(?:require|fetch|importScripts)\s*\(|\bimport\s*(?:\(|["'{*])|\bfrom\s*["']|\b(?:process|Buffer|__dirname|__filename)\b|\bnode:/); + expect(code).not.toMatch(/sourceMappingURL/); + expect(code).not.toContain('export default'); + }); + } + + for (const filename of runtime.filter((name) => name.endsWith('.mjs'))) { + test(`${filename} exposes the actual ESM namespace`, async () => { + // A variable file URL deliberately bypasses Vite source transformation. + const url = new URL(filename, dist).href; + const api: unknown = await import(/* @vite-ignore */ url); + expect(api).not.toHaveProperty('default'); + expect(api).toHaveProperty('solveLock'); + const node = await import('node:child_process'); + const output = node.execFileSync(process.execPath, ['--input-type=module', '-e', `import { solveLock } from ${JSON.stringify(url)}; import * as api from ${JSON.stringify(url)}; console.log(JSON.stringify([solveLock([6,2],[[0,-1],[0,0]]),api.solveLock([4,4],[[0,0],[0,0]]),Object.keys(api).sort()]));`], { encoding: 'utf8', cwd: fileURLToPath(dist) }); + expect(JSON.parse(output)).toEqual([[[0, -2]], [], ['LockInputError', 'SearchLimitError', 'solveLock']]); + }); + } + + test('manifest binds every runtime file and declaration to its bytes', async () => { + const raw: unknown = JSON.parse(await readFile(new URL('manifest.json', dist), 'utf8')); + const packageMetadata: unknown = JSON.parse(await readFile(new URL('../../package.json', import.meta.url), 'utf8')); + if (typeof packageMetadata !== 'object' || packageMetadata === null || !('version' in packageMetadata)) throw new Error('Malformed package metadata'); + expect(raw).toMatchObject({ schemaVersion: 1, package: 'gothic-lock-solver', version: packageMetadata.version, sourceSha: execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim() }); + if (typeof raw !== 'object' || raw === null || !('files' in raw) || typeof raw.files !== 'object' || raw.files === null) throw new Error('Malformed distribution manifest'); + expect(Object.keys(raw.files).sort()).toEqual((await readdir(dist)).filter((name) => name !== 'manifest.json').sort()); + for (const [name, digest] of Object.entries(raw.files)) { + expect(createHash('sha256').update(await readFile(new URL(name, dist))).digest('hex')).toBe(digest); + } + }); +}); diff --git a/tests/distribution/catalog.test.ts b/tests/distribution/catalog.test.ts new file mode 100644 index 0000000..1d706a0 --- /dev/null +++ b/tests/distribution/catalog.test.ts @@ -0,0 +1,20 @@ +import { execFileSync } from 'node:child_process'; +import { test, expect } from 'vitest'; +import { loadCatalog } from '../../benchmarks/catalog.ts'; +import { replayCommands } from '../../benchmarks/validation.ts'; + +for (const filename of ['gothic-lock-solver.mjs', 'gothic-lock-solver.min.mjs']) { + test(`${filename}: real Node loader solves all 45 fixtures at the exact minimum`, () => { + const catalog = loadCatalog(); + const code = `import { solveLock } from ${JSON.stringify(new URL(`../../dist/${filename}`, import.meta.url).href)};let input='';for await (const chunk of process.stdin) input+=chunk;console.log(JSON.stringify(JSON.parse(input).map(({definition:{state,links}})=>solveLock(state,links))));`; + const output = execFileSync(process.execPath, ['--input-type=module', '-e', code], { input: JSON.stringify(catalog), encoding: 'utf8' }); + const results: unknown = JSON.parse(output); + if (!Array.isArray(results)) throw new Error('Node consumer returned a malformed result'); + expect(results).toHaveLength(45); + for (const [index, fixture] of catalog.entries()) { + const commands: unknown = results[index]; + expect(commands).toHaveLength(fixture.expectedActions); + expect(replayCommands(fixture.definition, commands)).toEqual(fixture.definition.state.map(() => 4)); + } + }); +} diff --git a/tests/distribution/cli.test.ts b/tests/distribution/cli.test.ts new file mode 100644 index 0000000..c317546 --- /dev/null +++ b/tests/distribution/cli.test.ts @@ -0,0 +1,39 @@ +import { spawnSync } from 'node:child_process'; +import { copyFile, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { describe, expect, test } from 'vitest'; + +describe('standalone Node CLI artifact', () => { + test('runs copied bytes outside the repository with exit codes 0, 1 and 2 and --output', async () => { + const directory = await mkdtemp(join(tmpdir(), 'gothic-standalone-cli-')); + try { + const executable = join(directory, 'gothic-lock-solver.cli.mjs'); + const input = join(directory, 'lock.json'); + const output = join(directory, 'solution.json'); + await copyFile(new URL('../../dist/gothic-lock-solver.cli.mjs', import.meta.url), executable); + const contents = await readFile(executable, 'utf8'); + expect(contents.startsWith('#!/usr/bin/env node\n')).toBe(true); + // The sole Node file can import builtins; no sibling core modules or npm + // runtime package is present in this temporary consumer directory. + expect(contents).not.toMatch(/\bfrom\s*["'](?:\.|\/|gothic-lock-solver)/); + await writeFile(input, JSON.stringify({ state: [6, 2], links: [[0, -1], [0, 0]] })); + const solved = spawnSync(process.execPath, [executable, input, '--output', output], { cwd: directory, encoding: 'utf8' }); + expect(solved.status).toBe(0); + expect(solved.stderr).toBe(''); + expect(solved.stdout).toContain('Найдено команд: 1'); + expect(JSON.parse(await readFile(output, 'utf8'))).toMatchObject({ status: 'solved', commands: [{ plate: 1, direction: 'right', steps: 2 }] }); + await writeFile(input, JSON.stringify({ state: [1, 7], links: [[0, 1], [1, 0]] })); + const unreachable = spawnSync(process.execPath, [executable, input], { cwd: directory, encoding: 'utf8' }); + expect(unreachable.status).toBe(2); + expect(unreachable.stdout).toContain('Решение не найдено.'); + await writeFile(input, '{'); + const invalid = spawnSync(process.execPath, [executable, input], { cwd: directory, encoding: 'utf8' }); + expect(invalid.status).toBe(1); + expect(invalid.stdout).toBe(''); + expect(invalid.stderr).toContain('Некорректный JSON'); + } finally { + await rm(directory, { recursive: true, force: true }); + } + }); +}); diff --git a/tests/distribution/fixtures/consumer.mjs b/tests/distribution/fixtures/consumer.mjs new file mode 100644 index 0000000..e50d0b4 --- /dev/null +++ b/tests/distribution/fixtures/consumer.mjs @@ -0,0 +1,44 @@ +import assert from 'node:assert/strict'; +import { readFileSync, writeFileSync, existsSync } from 'node:fs'; +import { spawnSync } from 'node:child_process'; +import { delimiter, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { solveLock } from 'gothic-lock-solver'; +import * as GothicLockSolver from 'gothic-lock-solver'; + +assert.deepEqual(solveLock([6, 2], [[0, -1], [0, 0]]), [[0, -2]]); +assert.deepEqual(GothicLockSolver.solveLock([4, 4], [[0, 0], [0, 0]]), []); +assert.deepEqual(Object.keys(GothicLockSolver).sort(), ['LockInputError', 'SearchLimitError', 'solveLock']); +assert.ok(import.meta.resolve('gothic-lock-solver').includes('/node_modules/gothic-lock-solver/dist/')); +const manifest = JSON.parse(readFileSync(new URL('./node_modules/gothic-lock-solver/package.json', import.meta.url), 'utf8')); +assert.deepEqual(manifest.dependencies ?? {}, {}); +assert.deepEqual(manifest.optionalDependencies ?? {}, {}); +assert.deepEqual(manifest.peerDependencies ?? {}, {}); +assert.deepEqual(manifest.bundleDependencies ?? [], []); +assert.equal(manifest.private, false); +for (const lifecycle of ['preinstall', 'install', 'postinstall', 'prepare']) assert.equal(manifest.scripts?.[lifecycle], undefined); +assert.deepEqual(manifest.bin, { 'gothic-lock-solver': './dist/gothic-lock-solver.cli.mjs' }); +for (const path of ['gothic-lock-solver/dist/gothic-lock-solver.mjs', 'gothic-lock-solver/dist/gothic-lock-solver.min.mjs']) { + const api = await import(path); + assert.deepEqual(Object.keys(api).sort(), Object.keys(GothicLockSolver).sort()); + assert.deepEqual(api.solveLock([6, 2], [[0, -1], [0, 0]]), [[0, -2]]); +} +console.log(JSON.stringify({ node: process.version, installed: true, namespace: Object.keys(GothicLockSolver).sort() })); + +const input = new URL('./lock.json', import.meta.url); +const output = new URL('./solution.json', import.meta.url); +const executable = new URL('./node_modules/gothic-lock-solver/dist/gothic-lock-solver.cli.mjs', import.meta.url); +const bin = fileURLToPath(new URL('./node_modules/.bin/gothic-lock-solver', import.meta.url)); +assert.ok(existsSync(bin)); +writeFileSync(input, JSON.stringify({ state: [6, 2], links: [[0, -1], [0, 0]] })); +let cli = spawnSync(bin, [fileURLToPath(input), '--output', fileURLToPath(output)], { encoding: 'utf8', env: { ...process.env, PATH: `${dirname(process.execPath)}${delimiter}${process.env.PATH ?? ''}` } }); +assert.equal(cli.status, 0); +assert.equal(cli.stderr, ''); +assert.deepEqual(JSON.parse(readFileSync(output, 'utf8')).commands, [{ plate: 1, direction: 'right', steps: 2 }]); +writeFileSync(input, JSON.stringify({ state: [1, 7], links: [[0, 1], [1, 0]] })); +cli = spawnSync(process.execPath, [fileURLToPath(executable), fileURLToPath(input)], { encoding: 'utf8' }); +assert.equal(cli.status, 2); +writeFileSync(input, '{'); +cli = spawnSync(process.execPath, [fileURLToPath(executable), fileURLToPath(input)], { encoding: 'utf8' }); +assert.equal(cli.status, 1); +console.log(JSON.stringify({ installedCli: true, node: process.version, exitCodes: [0, 1, 2] })); diff --git a/tests/distribution/fixtures/consumer.mts b/tests/distribution/fixtures/consumer.mts new file mode 100644 index 0000000..85d32d8 --- /dev/null +++ b/tests/distribution/fixtures/consumer.mts @@ -0,0 +1,36 @@ +import { solveLock, LockInputError, SearchLimitError } from 'gothic-lock-solver'; +import * as GothicLockSolver from 'gothic-lock-solver'; +import { solveLock as minified } from 'gothic-lock-solver/dist/gothic-lock-solver.min.mjs'; +import { solveLock as readable } from 'gothic-lock-solver/dist/gothic-lock-solver.mjs'; +import type { Command, Delta, Link, Links, Position, State, SearchLimit } from 'gothic-lock-solver'; + +const state: State = [6, 2]; +const position: Position = 4; +const delta: Delta = -2; +const link: Link = -1; +const links: Links = [[0, link], [0, 0]]; +const commands: readonly Command[] | null = solveLock(state, links); +const dynamic: Position[] = [position, position]; +const dynamicLinks: Link[][] = [[0, 0], [0, 0]]; +const limit: SearchLimit | undefined = undefined; +void [commands, delta, limit, new LockInputError('input'), SearchLimitError]; +GothicLockSolver.solveLock(dynamic, dynamicLinks); +minified([6, 2], [[0, -1], [0, 0]]); +readable([4, 4], [[0, 0], [0, 0]]); +// @ts-expect-error The public ESM entry has no default export. +import defaultSolver from 'gothic-lock-solver'; +void defaultSolver; +// @ts-expect-error Internal search modules are not public package exports. +import internalSearch from 'gothic-lock-solver/dist/matrix-search.js'; +void internalSearch; +// @ts-expect-error Position zero is outside the lock model. +solveLock([0, 4], [[0, 0], [0, 0]]); +// @ts-expect-error Fixed input tuples require at least two positions. +solveLock([4], [[0]]); +// @ts-expect-error Fixed input tuples require a matching matrix. +solveLock([4, 4], [[0, 0]]); +// @ts-expect-error There is no third public configuration argument. +solveLock([4, 4], [[0, 0], [0, 0]], {}); +// @ts-expect-error A returned zero delta would be invalid. +const zero: Command = [0, 0]; +void zero; diff --git a/tests/distribution/fixtures/global-consumer.ts b/tests/distribution/fixtures/global-consumer.ts new file mode 100644 index 0000000..7c1ed7d --- /dev/null +++ b/tests/distribution/fixtures/global-consumer.ts @@ -0,0 +1,5 @@ +/// +const globalCommands: readonly GothicLockSolver.Command[] | null = GothicLockSolver.solveLock([6, 2], [[0, -1], [0, 0]]); +void globalCommands; +// @ts-expect-error The classic namespace exposes no default property. +GothicLockSolver.default; diff --git a/tests/e2e/catalog.ts b/tests/e2e/catalog.ts new file mode 100644 index 0000000..faf4cc5 --- /dev/null +++ b/tests/e2e/catalog.ts @@ -0,0 +1,52 @@ +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { join } from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { loadCatalog as loadPinnedCatalog } from '../../benchmarks/catalog.ts'; +import { array, replayCommands, tupleCommands } from '../../benchmarks/validation.ts'; +import { distDirectory } from './paths.ts'; + +export interface CatalogFixture { + readonly id: string; + readonly state: readonly number[]; + readonly links: readonly (readonly number[])[]; + readonly expectedActions: number; +} + +export function loadCatalog(): readonly CatalogFixture[] { + return loadPinnedCatalog().map(({ id, definition, expectedActions }) => ({ + id, + state: definition.state, + links: definition.links, + expectedActions, + })); +} + +export function verifySolution(fixture: CatalogFixture, commands: unknown): void { + const path = tupleCommands(commands); + assert.equal(path.length, fixture.expectedActions, `${fixture.id}: minimum actions`); + const definition = { + state: [...fixture.state], + links: fixture.links.map((row) => [...row]), + }; + // The benchmark oracle checks every intermediate division without production imports. + assert.deepEqual(replayCommands(definition, path), fixture.state.map(() => 4), `${fixture.id}: goal state`); +} + +export function loadExpectedSolutions(fixtures: readonly CatalogFixture[]): readonly unknown[] { + const moduleUrl = pathToFileURL(join(distDirectory, 'gothic-lock-solver.mjs')).href; + // A subprocess guarantees the native Node loader, outside Playwright's loader. + const code = `import { solveLock } from ${JSON.stringify(moduleUrl)}; + let input = ''; + for await (const chunk of process.stdin) input += chunk; + console.log(JSON.stringify(JSON.parse(input).map(({ state, links }) => solveLock(state, links))));`; + const output = execFileSync(process.execPath, ['--input-type=module', '-e', code], { + encoding: 'utf8', + input: JSON.stringify(fixtures), + }); + const raw: unknown = JSON.parse(output); + const results = array(raw, 'native Node solutions'); + assert.equal(results.length, fixtures.length); + for (const [index, fixture] of fixtures.entries()) verifySolution(fixture, results[index]); + return results; +} diff --git a/tests/e2e/consumers.spec.ts b/tests/e2e/consumers.spec.ts new file mode 100644 index 0000000..abd45a7 --- /dev/null +++ b/tests/e2e/consumers.spec.ts @@ -0,0 +1,195 @@ +import { copyFile, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { expect, test } from '@playwright/test'; +import type { BrowserContext, Page } from '@playwright/test'; +import { loadCatalog, loadExpectedSolutions, verifySolution } from './catalog.ts'; +import type { CatalogFixture } from './catalog.ts'; +import { distDirectory } from './paths.ts'; + +const catalog = loadCatalog(); +let nativeNodeSolutions: readonly unknown[] | undefined; +const reportedBrowserVersions = new Set(); +test.beforeEach(({ browser, browserName }, testInfo) => { + const version = browser.version(); + testInfo.annotations.push({ type: 'browser-version', description: `${browserName} ${version}` }); + if (!reportedBrowserVersions.has(browserName)) { + console.log(JSON.stringify({ browser: browserName, version })); + reportedBrowserVersions.add(browserName); + } +}); +const formats = [ + { fixture: 'module.html', bundle: 'gothic-lock-solver.mjs', classic: false }, + { fixture: 'module.min.html', bundle: 'gothic-lock-solver.min.mjs', classic: false }, + { fixture: 'classic.html', bundle: 'gothic-lock-solver.js', classic: true }, + { fixture: 'classic.min.html', bundle: 'gothic-lock-solver.min.js', classic: true }, +]; + +function observeRequests(context: BrowserContext, page: Page) { + const requests: string[] = []; + const failures: string[] = []; + const pageErrors: string[] = []; + context.on('request', (request) => requests.push(request.url())); + context.on('requestfailed', (request) => failures.push(request.url())); + page.on('pageerror', (error) => pageErrors.push(error.message)); + return { requests, failures, pageErrors }; +} + +// Playwright serializes this function into the consumer page. All invocation logic +// stays here; the only solver available to it came from the HTML's dist import. +function exerciseConsumer(fixtures: readonly CatalogFixture[]) { + const consumer: unknown = Reflect.get(globalThis, 'consumer'); + if (typeof consumer !== 'object' || consumer === null) { + throw new Error('The plain HTML consumer did not load'); + } + const solveLock: unknown = Reflect.get(consumer, 'solveLock'); + if (typeof solveLock !== 'function') { + throw new Error('Missing public solveLock function'); + } + const invoke = (state: unknown, links: unknown): unknown => Reflect.apply( + solveLock, undefined, [state, links], + ); + const publicExports: unknown = Reflect.get(consumer, 'exports'); + const namedMatchesNamespace: unknown = Reflect.get(consumer, 'namedMatchesNamespace'); + const hasClassicGlobal: unknown = Reflect.get(consumer, 'hasClassicGlobal'); + let invalidError: string | null = null; + try { + invoke([0, 4], [[0, 0], [0, 0]]); + } catch (error) { + invalidError = error instanceof Error ? error.name : 'non-Error thrown'; + } + return { + publicExports, + namedMatchesNamespace, + hasClassicGlobal, + signed: invoke([6, 2], [[0, -1], [0, 0]]), + opened: invoke([4, 4], [[0, 0], [0, 0]]), + impossible: invoke([1, 1, 1], [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]]), + invalidError, + catalog: fixtures.map((fixture) => { + const state = Object.freeze([...fixture.state]); + const links = Object.freeze(fixture.links.map((row) => Object.freeze([...row]))); + const commands = invoke(state, links); + return { id: fixture.id, commands, repeat: invoke(state, links), state, links }; + }), + }; +} + +for (const format of formats) { + test(`${format.bundle}: static HTML, public contract, and all 45 locks`, async ({ context, page }) => { + nativeNodeSolutions ??= loadExpectedSolutions(catalog); + const observed = observeRequests(context, page); + const bundlePath = `/dist/${format.bundle}`; + const fixturePath = `/tests/e2e/fixtures/${format.fixture}`; + const responsePromise = page.waitForResponse((response) => new URL(response.url()).pathname === bundlePath); + await page.goto(fixturePath); + await expect(page.locator('#status')).toHaveText('ready'); + const response = await responsePromise; + expect(response.status()).toBe(200); + expect(response.headers()['content-type']).toBe('application/javascript; charset=utf-8'); + expect(await response.body()).toEqual(await readFile(join(distDirectory, format.bundle))); + + const result = await page.evaluate(exerciseConsumer, catalog); + expect(result.publicExports).toContain('solveLock'); + expect(result.publicExports).not.toContain('default'); + expect(result.namedMatchesNamespace).toBe(true); + expect(result.hasClassicGlobal).toBe(format.classic); + expect(result.signed).toEqual([[0, -2]]); + expect(result.opened).toEqual([]); + expect(result.impossible).toBeNull(); + expect(result.invalidError).toBe('LockInputError'); + expect(result.catalog).toHaveLength(45); + for (const [index, entry] of result.catalog.entries()) { + const fixture = catalog[index]; + if (fixture === undefined) throw new Error(`Unexpected result ${entry.id}`); + expect(entry.id).toBe(fixture.id); + verifySolution(fixture, entry.commands); + expect(entry.commands, `${fixture.id}: exact format and environment equivalence`).toEqual(nativeNodeSolutions[index]); + expect(entry.repeat, `${fixture.id}: deterministic repeat`).toEqual(entry.commands); + expect(entry.state, `${fixture.id}: state mutation`).toEqual(fixture.state); + expect(entry.links, `${fixture.id}: links mutation`).toEqual(fixture.links); + } + expect(observed.pageErrors).toEqual([]); + expect(observed.failures).toEqual([]); + expect(observed.requests.sort()).toEqual([ + `http://127.0.0.1:4177${fixturePath}`, + `http://127.0.0.1:4177${bundlePath}`, + ].sort()); + }); +} + +for (const minified of [false, true]) { + const suffix = minified ? '.min' : ''; + test(`module Worker imports gothic-lock-solver${suffix}.mjs`, async ({ context, page }) => { + const observed = observeRequests(context, page); + await page.goto('/tests/e2e/fixtures/worker.html'); + const results = await page.evaluate(async (workerFile) => { + const worker = new Worker(`./${workerFile}`, { type: 'module' }); + const cases = [ + { state: [6, 2], links: [[0, -1], [0, 0]] }, + { state: [4, 4], links: [[0, 0], [0, 0]] }, + ]; + return await new Promise((resolve, reject) => { + const received: unknown[] = []; + worker.onerror = (event) => { + worker.terminate(); + reject(new Error(event.message)); + }; + worker.onmessage = (event: MessageEvent) => { + received.push(event.data); + if (received.length === cases.length) { + worker.terminate(); + resolve(received); + } else { + worker.postMessage(cases[received.length]); + } + }; + worker.postMessage(cases[0]); + }); + }, `worker${suffix}.mjs`); + expect(results).toEqual([[[0, -2]], []]); + expect(observed.pageErrors).toEqual([]); + expect(observed.failures).toEqual([]); + expect(observed.requests.sort()).toEqual([ + 'http://127.0.0.1:4177/tests/e2e/fixtures/worker.html', + `http://127.0.0.1:4177/tests/e2e/fixtures/worker${suffix}.mjs`, + `http://127.0.0.1:4177/dist/gothic-lock-solver${suffix}.mjs`, + ].sort()); + }); + + test(`classic gothic-lock-solver${suffix}.js loads through file://`, async ({ context, page }) => { + const observed = observeRequests(context, page); + const isolatedDirectory = await mkdtemp(join(tmpdir(), 'gothic-classic-')); + try { + const fixtureName = `classic${suffix}.html`; + const bundleName = `gothic-lock-solver${suffix}.js`; + const html = await readFile(new URL(`./fixtures/${fixtureName}`, import.meta.url), 'utf8'); + await writeFile(join(isolatedDirectory, fixtureName), html.replace('src="../../../dist/', 'src="./')); + await copyFile(join(distDirectory, bundleName), join(isolatedDirectory, bundleName)); + const fixtureUrl = pathToFileURL(join(isolatedDirectory, fixtureName)).href; + const bundleUrl = pathToFileURL(join(isolatedDirectory, bundleName)).href; + await page.goto(fixtureUrl); + await expect(page.locator('#status')).toHaveText('ready'); + expect(page.url()).toBe(fixtureUrl); + const externalScript = page.locator('script[src]'); + await expect(externalScript).toHaveCount(1); + await expect(externalScript).toHaveJSProperty('src', bundleUrl); + await expect(externalScript).toHaveJSProperty('type', ''); + const result = await page.evaluate(exerciseConsumer, []); + expect(result.signed).toEqual([[0, -2]]); + expect(result.opened).toEqual([]); + expect(result.impossible).toBeNull(); + expect(result.invalidError).toBe('LockInputError'); + expect(result.hasClassicGlobal).toBe(true); + expect(observed.pageErrors).toEqual([]); + expect(observed.failures).toEqual([]); + // Firefox does not emit request events for file:// loads. Verify the actual + // local document/script above, and reject unexpected URLs when events exist. + const allowedRequests = new Set([fixtureUrl, bundleUrl]); + expect(observed.requests.filter((url) => !allowedRequests.has(url))).toEqual([]); + } finally { + await rm(isolatedDirectory, { recursive: true, force: true }); + } + }); +} diff --git a/tests/e2e/fixtures/classic.html b/tests/e2e/fixtures/classic.html new file mode 100644 index 0000000..6f3f64a --- /dev/null +++ b/tests/e2e/fixtures/classic.html @@ -0,0 +1,17 @@ + + + + + Plain HTML classic consumer + loading + + + diff --git a/tests/e2e/fixtures/classic.min.html b/tests/e2e/fixtures/classic.min.html new file mode 100644 index 0000000..db22e5e --- /dev/null +++ b/tests/e2e/fixtures/classic.min.html @@ -0,0 +1,17 @@ + + + + + Plain HTML minified classic consumer + loading + + + diff --git a/tests/e2e/fixtures/module.html b/tests/e2e/fixtures/module.html new file mode 100644 index 0000000..9780752 --- /dev/null +++ b/tests/e2e/fixtures/module.html @@ -0,0 +1,18 @@ + + + + + Plain HTML ESM consumer + loading + + diff --git a/tests/e2e/fixtures/module.min.html b/tests/e2e/fixtures/module.min.html new file mode 100644 index 0000000..4cb6da1 --- /dev/null +++ b/tests/e2e/fixtures/module.min.html @@ -0,0 +1,18 @@ + + + + + Plain HTML minified ESM consumer + loading + + diff --git a/tests/e2e/fixtures/worker.html b/tests/e2e/fixtures/worker.html new file mode 100644 index 0000000..2b660d8 --- /dev/null +++ b/tests/e2e/fixtures/worker.html @@ -0,0 +1,7 @@ + + + + + Plain HTML module Worker consumer + ready + diff --git a/tests/e2e/fixtures/worker.min.mjs b/tests/e2e/fixtures/worker.min.mjs new file mode 100644 index 0000000..b70b6d2 --- /dev/null +++ b/tests/e2e/fixtures/worker.min.mjs @@ -0,0 +1,5 @@ +import { solveLock } from '../../../dist/gothic-lock-solver.min.mjs'; + +self.addEventListener('message', ({ data }) => { + self.postMessage(solveLock(data.state, data.links)); +}); diff --git a/tests/e2e/fixtures/worker.mjs b/tests/e2e/fixtures/worker.mjs new file mode 100644 index 0000000..42368dc --- /dev/null +++ b/tests/e2e/fixtures/worker.mjs @@ -0,0 +1,5 @@ +import { solveLock } from '../../../dist/gothic-lock-solver.mjs'; + +self.addEventListener('message', ({ data }) => { + self.postMessage(solveLock(data.state, data.links)); +}); diff --git a/tests/e2e/paths.ts b/tests/e2e/paths.ts new file mode 100644 index 0000000..fb498e1 --- /dev/null +++ b/tests/e2e/paths.ts @@ -0,0 +1,10 @@ +import { isAbsolute } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const override = process.env['GOTHIC_DIST_DIRECTORY']; +if (override !== undefined && !isAbsolute(override)) { + throw new Error('GOTHIC_DIST_DIRECTORY must be an absolute directory path.'); +} + +// Package verification points this at the installed tarball's dist directory. +export const distDirectory = override ?? fileURLToPath(new URL('../../dist/', import.meta.url)); diff --git a/tests/e2e/server.ts b/tests/e2e/server.ts new file mode 100644 index 0000000..467f133 --- /dev/null +++ b/tests/e2e/server.ts @@ -0,0 +1,75 @@ +import { access, readFile } from 'node:fs/promises'; +import { createServer } from 'node:http'; +import { join } from 'node:path'; +import { distDirectory } from './paths.ts'; + +const root = new URL('../../', import.meta.url); +const runtimeFiles = [ + 'gothic-lock-solver.mjs', + 'gothic-lock-solver.min.mjs', + 'gothic-lock-solver.js', + 'gothic-lock-solver.min.js', +]; +const fixtureFiles = [ + 'module.html', + 'module.min.html', + 'classic.html', + 'classic.min.html', + 'worker.html', + 'worker.mjs', + 'worker.min.mjs', +]; +const paths = new Set([ + ...runtimeFiles.map((file) => `/dist/${file}`), + ...fixtureFiles.map((file) => `/tests/e2e/fixtures/${file}`), +]); + +// Starting this server never builds or transforms the artifacts being tested. +await Promise.all(runtimeFiles.map(async (file) => { + try { + await access(join(distDirectory, file)); + } catch (cause) { + throw new Error(`Missing ${join(distDirectory, file)}; run pnpm build before pnpm test:e2e.`, { cause }); + } +})); + +const server = createServer((request, response) => { + const pathname = new URL(request.url ?? '/', 'http://127.0.0.1:4177').pathname; + if (request.method !== 'GET' && request.method !== 'HEAD') { + response.writeHead(405, { Allow: 'GET, HEAD' }); + response.end(); + return; + } + if (pathname === '/health') { + response.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); + response.end(request.method === 'HEAD' ? undefined : 'ready'); + return; + } + if (!paths.has(pathname)) { + response.writeHead(404); + response.end(); + return; + } + const file = pathname.startsWith('/dist/') + ? join(distDirectory, pathname.slice('/dist/'.length)) + : new URL(pathname.slice(1), root); + void readFile(file).then((bytes) => { + response.writeHead(200, { + 'Content-Type': pathname.endsWith('.html') + ? 'text/html; charset=utf-8' + : 'application/javascript; charset=utf-8', + 'Content-Length': bytes.byteLength, + 'Cache-Control': 'no-store', + 'X-Content-Type-Options': 'nosniff', + }); + response.end(request.method === 'HEAD' ? undefined : bytes); + }, () => { + response.writeHead(500); + response.end(); + }); +}); + +server.listen(4177, '127.0.0.1'); +process.once('SIGTERM', () => { + server.close(); +}); diff --git a/tests/helpers/oracle.ts b/tests/helpers/oracle.ts new file mode 100644 index 0000000..88c215c --- /dev/null +++ b/tests/helpers/oracle.ts @@ -0,0 +1,55 @@ +/** Independent oracle: imports no production transitions, encodings or search. */ +export interface OracleLock { + readonly state: readonly number[]; + readonly links: readonly (readonly number[])[]; +} + +function element(values: readonly T[], index: number): T { + const value = values[index]; + if (value === undefined) throw new Error(`Missing oracle element ${index}`); + return value; +} + +export function unitMove(state: readonly number[], links: OracleLock['links'], source: number, direction: number): number[] | null { + const row = element(links, source); + const next = state.map((position, target) => position + direction * (source === target ? 1 : element(row, target))); + return next.some((position) => position < 1 || position > 7) ? null : next; +} + +export function replay(lock: OracleLock, commands: readonly (readonly [number, number])[]): number[] { + let state = [...lock.state]; + for (const [index, delta] of commands) { + if (!Number.isInteger(index) || index < 0 || index >= state.length || !Number.isInteger(delta) || delta === 0 || Math.abs(delta) > 6) { + throw new Error('Invalid command'); + } + for (let step = 0; step < Math.abs(delta); step += 1) { + const next = unitMove(state, lock.links, index, Math.sign(delta)); + if (next === null) throw new Error('Blocked intermediate unit movement'); + state = next; + } + } + return state; +} + +export function referenceActions(lock: OracleLock): number | null { + const queue = [{ state: [...lock.state], depth: 0 }]; + const visited = new Set([lock.state.join(',')]); + for (const current of queue) { + if (current.state.every((position) => position === 4)) return current.depth; + for (let source = 0; source < current.state.length; source += 1) { + for (const direction of [1, -1]) { + let next = current.state; + for (let steps = 1; steps <= 6; steps += 1) { + const moved = unitMove(next, lock.links, source, direction); + if (moved === null) break; + next = moved; + const key = next.join(','); + if (visited.has(key)) continue; + visited.add(key); + queue.push({ state: next, depth: current.depth + 1 }); + } + } + } + } + return null; +} diff --git a/tests/types/assets.d.ts b/tests/types/assets.d.ts new file mode 100644 index 0000000..76af98e --- /dev/null +++ b/tests/types/assets.d.ts @@ -0,0 +1,4 @@ +declare module '*.json?raw' { + const content: string; + export default content; +} diff --git a/tests/types/core-contract.ts b/tests/types/core-contract.ts new file mode 100644 index 0000000..df0857a --- /dev/null +++ b/tests/types/core-contract.ts @@ -0,0 +1,37 @@ +import { solveLock } from '../../src/index.ts'; +import type { Command, Delta, Link, Links, Position, State } from '../../src/index.ts'; +const state = [6, 2] as const satisfies State; +const links = [[0, -1], [0, 0]] as const satisfies Links; +const result: readonly Command[] | null = solveLock(state, links); +void result; +const dynamicState: readonly Position[] = [1, 7, 4]; +const dynamicLinks: readonly (readonly Link[])[] = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]; +solveLock(dynamicState, dynamicLinks); +// @ts-expect-error one plate is not a fixed valid state +solveLock([4], [[0]]); +// @ts-expect-error no state positions outside 1..7 +solveLock([0, 4], [[0, 0], [0, 0]]); +// @ts-expect-error N is inferred only from state; row count must be N +solveLock([4, 4], [[0, 0], [0, 0], [0, 0]]); +// @ts-expect-error fixed tuples require square rows +solveLock([4, 4], [[0, 0, 0], [0, 0, 0]]); +// @ts-expect-error short rows must be rejected +solveLock(state, [[0], [0]]); +// @ts-expect-error links are -1, 0 or 1 +solveLock(state, [[0, 2], [0, 0]]); +// @ts-expect-error old object wrapper is not public +solveLock({ state, links }); +// @ts-expect-error public solver has no third options argument +solveLock(state, links, { algorithm: 'bfs' }); +// @ts-expect-error zero delta is not a command +const invalidDelta: Delta = 0; +void invalidDelta; +if (result !== null) { + // @ts-expect-error returned list is readonly + result.push([0, 1]); + const first = result[0]; + if (first !== undefined) { + // @ts-expect-error returned command is readonly + first[1] = 1; + } +} diff --git a/tests/unit/benchmark.test.ts b/tests/unit/benchmark.test.ts new file mode 100644 index 0000000..d081c78 --- /dev/null +++ b/tests/unit/benchmark.test.ts @@ -0,0 +1,155 @@ +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { array, commandMetrics, replayCommands, legacyCommands, record } from '../../benchmarks/validation.ts'; +import { compareMetrics, confirmPerformance, performanceVerdict, statistics } from '../../benchmarks/comparison.ts'; +import { catalogSha256, loadCatalog, sha256, verifySource, verifyReferenceSource } from '../../benchmarks/catalog.ts'; +import { publishReport, readSnapshot, runComparison, writeSnapshot, type Implementation } from '../../benchmarks/harness.ts'; + +import { parseBenchmarkArgs } from '../../benchmarks/cli.ts'; + +const definition = { state: [3, 4], links: [[0, 0], [0, 0]] }; +const commands: readonly (readonly [number, number])[] = [[0, 1]]; +const fixture = { id: 'unit', title: 'Unit', definition, expectedActions: 1, + baselineCommands: commands, historicalReference: { commands, A: 1, U: 1, C: 1, plateSwitches: 0 } }; +const implementation = (solve: Implementation['solve'] = () => commands): Implementation => ({ + label: 'unit', solve, normalize: (value) => value, requiresOptimality: true, +}); + +describe('benchmark replay independent of production transitions', () => { + it('uses zero-based numeric deltas, simultaneous outgoing links and every intermediate click', () => { + expect(replayCommands(definition, commands)).toEqual([4, 4]); + expect(replayCommands({ state: [2, 5, 6], links: [[0, -1, 0], [0, 0, 1], [0, 0, 0]] }, [[0, 2]])).toEqual([4, 3, 6]); + expect(() => replayCommands({ state: [6, 6], links: [[0, 1], [0, 0]] }, [[0, 2]])).toThrow(/blocked/i); + expect(definition.state).toEqual([3, 4]); + }); + it('rejects invalid tuple and command coordinates', () => { + for (const invalid of [[[0, 0]], [[-1, 1]], [[2, 1]], [[0, 7]], [[0, 1.5]], [[0, 1, 2]], [{ plate: 1 }]]) { + expect(() => replayCommands(definition, invalid)).toThrow(/command/i); + } + }); + it('measures A/U/C/switches and converts legacy left to positive numeric delta', () => { + expect(commandMetrics([[0, 2], [1, -3], [0, 1]])).toEqual({ A: 3, U: 2, C: 6, plateSwitches: 2 }); + expect(legacyCommands([{ plate: 1, direction: 'left', steps: 2 }, { plate: 2, direction: 'right', steps: 3 }])).toEqual([[0, 2], [1, -3]]); + }); +}); + +describe('deterministic and environmental verdicts stay separate', () => { + it('fails every metric regression even if another metric improves', () => { + expect(compareMetrics({ A: 4, U: 1, C: 6, plateSwitches: 0 }, { A: 3, U: 2, C: 8, plateSwitches: 1 }).quality).toBe('regression'); + expect(compareMetrics({ A: 3, U: 3, C: 7, plateSwitches: 1 }, { A: 3, U: 2, C: 8, plateSwitches: 1 }).quality).toBe('regression'); + expect(compareMetrics({ A: 3, U: 2, C: 8, plateSwitches: 2 }, { A: 3, U: 2, C: 8, plateSwitches: 1 }).quality).toBe('regression'); + }); + it('calibrates uncertainty from self-comparison and never uses historical times as proof', () => { + expect(performanceVerdict([10, 10, 10, 10, 10], [10, 10, 10, 10, 10], [10, 10, 10, 10, 10], [10, 10, 10, 10, 10]).verdict).toBe('passed'); + expect(performanceVerdict([20, 20, 20, 20, 20], [10, 10, 10, 10, 10], [10, 10, 10, 10, 10], [10, 10, 10, 10, 10]).verdict).toBe('regression'); + expect(performanceVerdict([20], [10], [10], [10]).verdict).toBe('inconclusive'); + expect(performanceVerdict([10, 20, 10, 20, 10], [10, 10, 10, 10, 10], [5, 20, 5, 20, 5], [10, 10, 10, 10, 10]).verdict).toBe('inconclusive'); + expect(performanceVerdict([10, 10, 10, 10, 10], [10, 10, 10, 10, 10], [], []).verdict).toBe('inconclusive'); + }); + it('requires repeat confirmation of an environmental regression and preserves conflicting signals', () => { + expect(confirmPerformance('regression', 'regression')).toBe('regression'); + expect(confirmPerformance('regression', 'passed')).toBe('inconclusive'); + expect(confirmPerformance('inconclusive', 'passed')).toBe('inconclusive'); + expect(confirmPerformance('passed', 'passed')).toBe('passed'); + }); + it('retains raw sample order and computes nearest-rank p95', () => { + const samples = [9, 1, 5, 3]; + expect(statistics(samples)).toEqual({ min: 1, median: 4, p95: 9, max: 9 }); + expect(samples).toEqual([9, 1, 5, 3]); + for (const bad of [[], [-1], [NaN]]) expect(() => statistics(bad)).toThrow(/sample/i); + }); +}); + +describe('evidence trust boundaries', () => { + it('retains all 45 immutable fixtures and original source pins', () => { + expect(loadCatalog()).toHaveLength(45); + expect(verifySource(new URL('../../benchmarks/baseline/', import.meta.url))).toHaveProperty('revision', 'a87e739a22ccc4215d6d9b14fa06b0738171cb60'); + expect(verifySource(new URL('../../benchmarks/reference-matrix/', import.meta.url))).toHaveProperty('revision', '6b1cfbc13bcec68f609d45d5dc97504dd84f1760'); + }); + it('rejects source changes before external code can execute', async () => { + const root = await mkdtemp(join(tmpdir(), 'benchmark-source-')); + try { + await writeFile(join(root, 'solver.mjs'), 'original'); + await writeFile(join(root, 'manifest.json'), JSON.stringify({ revision: 'test', files: { 'solver.mjs': sha256('original') } })); + verifySource(root); + await writeFile(join(root, 'solver.mjs'), 'modified'); + expect(() => verifySource(root)).toThrow(/hash/i); + } finally { await rm(root, { recursive: true, force: true }); } + }); + it('checks upstream revision, entire file and marked block independently', () => { + const block = '/* solver-start */ function solveLock() {} /* solver-end */'; + const html = ``; + const pin = { revision: 'pin', solver: { path: 'index.html', sha256: sha256(html), blockSha256: sha256(block), startMarker: '/* solver-start', endMarker: '/* solver-end */' } }; + expect(verifyReferenceSource({ revision: 'pin', html }, pin)).toBe(block); + expect(() => verifyReferenceSource({ revision: 'bad', html }, pin)).toThrow(/revision/i); + expect(() => verifyReferenceSource({ revision: 'pin', html: 'modified' }, pin)).toThrow(/hash/i); + expect(() => verifyReferenceSource({ revision: 'pin', html }, { ...pin, solver: { ...pin.solver, blockSha256: 'wrong' } })).toThrow(/block hash/i); + }); + it('never overwrites a quality snapshot', async () => { + const root = await mkdtemp(join(tmpdir(), 'benchmark-snapshot-')); + const path = join(root, 'quality.json'); + try { + await writeSnapshot(path, { reason: 'initial', locks: [] }); + const initial = await readFile(path, 'utf8'); + await expect(writeSnapshot(path, { reason: 'replacement', locks: [] })).rejects.toThrow(); + expect(await readFile(path, 'utf8')).toBe(initial); + } finally { await rm(root, { recursive: true, force: true }); } + }); + it('validates warmups, measured paths, determinism, input immutability and exact action optimum', () => { + let calls = 0; + const setup = { locks: [fixture], candidate: implementation(), baseline: implementation(), repetitions: 5, warmups: 1 }; + expect(runComparison(setup).quality).toBe('passed'); + expect(() => runComparison({ ...setup, candidate: implementation(() => []) })).toThrow(/target|minimum/i); + expect(() => runComparison({ ...setup, candidate: implementation((input) => { input.state[0] = 4; return commands; }) })).toThrow(/mutat/i); + expect(() => runComparison({ ...setup, candidate: implementation(() => ++calls === 1 ? commands : [[0, 2], [0, -1]]) })).toThrow(/minimum|determin/i); + }); +}); + +it('requires explicit baseline inputs and rejects unsafe or ambiguous CLI settings', () => { + expect(() => parseBenchmarkArgs([], true)).toThrow(/explicit/); + expect(parseBenchmarkArgs(['--baseline-module', '/base.mjs', '--baseline-snapshot', '/quality.json', '--baseline-format', 'legacy'], true).baselineFormat).toBe('legacy'); + for (const args of [['--warmups', '-1'], ['--warmups', '01'], ['--repetitions', '0'], ['--warmups', '1', '--warmups', '2'], ['--reference'], ['--unknown', '1']]) { + expect(() => parseBenchmarkArgs(args)).toThrow(/argument|integer/); + } +}); + +it('checks target-branch snapshot against the live base independently of candidate expectations', () => { + const baselineSnapshot = { schemaVersion: 1, fixtureSha256: catalogSha256, source: {}, reason: 'test baseline', + locks: [{ id: 'unit', optimumA: 1, ...commandMetrics(commands), commands: [[0, 2], [0, -1]] as const }] }; + expect(() => runComparison({ locks: [fixture], candidate: implementation(), baseline: implementation(), baselineSnapshot, repetitions: 1, warmups: 0 })).toThrow(/snapshot path drift/); + expect(readSnapshot(new URL('../../benchmarks/snapshots/reference-matrix.json', import.meta.url).pathname).locks).toHaveLength(45); +}); + +it('publishes report, Markdown and hash-linked release evidence without changing earlier reports', async () => { + const root = await mkdtemp(join(tmpdir(), 'benchmark-report-')); + try { + const report = runComparison({ locks: [fixture], candidate: implementation(), baseline: implementation(), repetitions: 1, warmups: 0 }); + const first = await publishReport(report, root); const previous = await readFile(first.jsonPath, 'utf8'); + const second = await publishReport(report, root); + expect(second.directory).not.toBe(first.directory); + expect(await readFile(first.jsonPath, 'utf8')).toBe(previous); + const parsed: unknown = JSON.parse(await readFile(first.evidencePath, 'utf8')); + const evidence = record(parsed); + expect(record(record(evidence['reports'])['json'])['sha256']).toBe(sha256(previous)); + expect(record(array(record(evidence['qualitySnapshot'])['locks'])[0])['commands']).toEqual(commands); + expect(await readFile(first.markdownPath, 'utf8')).toMatch(/inconclusive/); + } finally { await rm(root, { recursive: true, force: true }); } +}); + +it('isolated RSS worker restricts its workload to the selected smoke fixtures', async () => { + const { measureRss } = await import('../../benchmarks/memory.ts'); + const result = measureRss({ name: 'baseline', module: new URL('../../benchmarks/reference-matrix/src/index.mjs', import.meta.url).pathname, format: 'legacy' }, 'cor-galom-chest,cor-galom-bedroom-chest'); + expect(result.lockCount).toBe(2); + expect(result.peakRssBytes).toBeGreaterThan(0); + expect(result.startupRssBytes).toBeGreaterThan(0); +}); + +it('names the process-image RSS metric and preserves launch-floor diagnostics', async () => { + const { measureRss } = await import('../../benchmarks/memory.ts'); + const result = measureRss({ name: 'baseline', module: new URL('../../benchmarks/reference-matrix/src/index.mjs', import.meta.url).pathname, format: 'legacy' }, 'cor-galom-chest'); + expect(result.peakRssMetric).toBe(process.platform === 'linux' ? 'linux-proc-VmHWM' : 'node-resourceUsage-maxRSS'); + expect(result.startupPeakRssBytes).toBeGreaterThan(0); + expect(result.resourceUsageMaxRssBytes).toBeGreaterThan(0); +}); diff --git a/tests/unit/cli.test.ts b/tests/unit/cli.test.ts new file mode 100644 index 0000000..b30878d --- /dev/null +++ b/tests/unit/cli.test.ts @@ -0,0 +1,85 @@ +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { afterEach, describe, expect, it } from 'vitest'; +import { runCli } from '../../cli/cli.ts'; + +const directories: string[] = []; +afterEach(async () => { + await Promise.all(directories.splice(0).map((directory) => rm(directory, { recursive: true, force: true }))); +}); + +async function fixture(contents: unknown) { + const directory = await mkdtemp(join(tmpdir(), 'gothic-cli-')); + directories.push(directory); + const input = join(directory, 'lock.json'); + await writeFile(input, JSON.stringify(contents)); + return { directory, input, output: join(directory, 'solution.json') }; +} + +async function run(args: readonly string[]) { + let stdout = ''; + let stderr = ''; + const code = await runCli(args, { + stdout: { write: (text) => { stdout += text; } }, + stderr: { write: (text) => { stderr += text; } }, + }); + return { code, stdout, stderr }; +} + +describe('CLI file adapter', () => { + it('prints grouped actions while leaving files untouched unless --output is requested', async () => { + const { input, output } = await fixture({ state: [1, 7], links: [[0, 0], [0, 0]] }); + expect(await run([input])).toEqual({ code: 0, stdout: 'Найдено команд: 2\n1. Пластина 1 - влево x3\n2. Пластина 2 - вправо x3\n', stderr: '' }); + await expect(readFile(output)).rejects.toMatchObject({ code: 'ENOENT' }); + }); + + it('creates and overwrites the existing structured JSON contract', async () => { + const { input, output } = await fixture({ state: [1, 7], links: [[0, 0], [0, 0]] }); + const expected = { + status: 'solved', initialState: [1, 7], targetState: [4, 4], + commands: [{ plate: 1, direction: 'left', steps: 3 }, { plate: 2, direction: 'right', steps: 3 }], + finalState: [4, 4], metrics: { commands: 2, divisions: 6, plateSwitches: 1 }, + }; + for (const stale of ['', 'stale'.repeat(1000)]) { + await writeFile(output, stale); + expect((await run([input, '--output', output])).code).toBe(0); + expect(await readFile(output, 'utf8')).toBe(`${JSON.stringify(expected, null, 2)}\n`); + } + }); + + it('distinguishes an already open lock from a proven unreachable goal', async () => { + const open = await fixture({ state: [4, 4], links: [[0, 0], [0, 0]] }); + expect(await run([open.input])).toEqual({ code: 0, stdout: 'Замок уже открыт.\n', stderr: '' }); + const blocked = await fixture({ state: [1, 7], links: [[0, 1], [1, 0]] }); + expect(await run([blocked.input, '--output', blocked.output])).toEqual({ code: 2, stdout: 'Решение не найдено.\n', stderr: '' }); + const result: unknown = JSON.parse(await readFile(blocked.output, 'utf8')); + expect(result).toEqual({ status: 'unsolvable', initialState: [1, 7], targetState: [4, 4], commands: [] }); + }); + + it.each([[], [''], ['--unknown'], ['x', '--output'], ['x', 'extra'], ['x', '--output', ''], ['x', '--output', 'y', 'extra']])('rejects malformed command line %j', async (...args) => { + const result = await run(args); + expect(result.code).toBe(1); + expect(result.stdout).toBe(''); + expect(result.stderr).toMatch(/^Ошибка: Использование:/u); + }); + + it.each([null, [], {}, { state: [0, 4], links: [[0, 0], [0, 0]] }])('rejects malformed input %j without reporting unsolvable', async (input) => { + const files = await fixture(input); + const result = await run([files.input]); + expect(result.code).toBe(1); + expect(result.stdout).toBe(''); + expect(result.stderr).toMatch(/^Ошибка:/u); + }); + + it('reports JSON, input IO and output IO failures without a success message', async () => { + const files = await fixture({ state: [4, 4], links: [[0, 0], [0, 0]] }); + for (const args of [[join(files.directory, 'missing.json')], [files.input, '--output', files.directory]]) { + const result = await run(args); + expect(result).toMatchObject({ code: 1, stdout: '' }); + expect(result.stderr).toMatch(/^Ошибка:/u); + } + await writeFile(files.input, '{'); + expect(await run([files.input])).toMatchObject({ code: 1, stdout: '', stderr: expect.stringContaining('Некорректный JSON') }); + }); +}); diff --git a/tests/unit/core-contract.test.ts b/tests/unit/core-contract.test.ts new file mode 100644 index 0000000..53d8dc7 --- /dev/null +++ b/tests/unit/core-contract.test.ts @@ -0,0 +1,102 @@ +import { describe, expect, it } from 'vitest'; +import * as api from '../../src/index.ts'; +import type { Link, Position } from '../../src/index.ts'; +import { referenceActions, replay, unitMove } from '../helpers/oracle.ts'; + +const inverse = [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]] as const; +const independent = (state: readonly Position[]) => state.map(() => state.map((): Link => 0)); + +function solveUnknown(state: unknown, links: unknown): unknown { + return Reflect.apply(api.solveLock, undefined, [state, links]); +} + +describe('public tuple contract', () => { + it('exports only the named solver and documented error classes', () => { + expect(Object.keys(api).sort()).toEqual(['LockInputError', 'SearchLimitError', 'solveLock']); + expect(api.solveLock.length).toBe(2); + }); + it('uses numeric pin delta and links[source][target] without reverse influence', () => { + expect(api.solveLock([6, 2], [[0, -1], [0, 0]])).toEqual([[0, -2]]); + expect(api.solveLock([4, 2], [[0, -1], [0, 0]])).toEqual([[1, 2]]); + }); + it('applies direct effects once without cascades', () => { + const state = [3, 3, 4] as const; + const links = [[0, 1, 0], [0, 0, 1], [0, 0, 0]] as const; + expect(api.solveLock(state, links)).toEqual([[0, 1]]); + }); + it('does not mutate frozen input and returns independent repeatable results', () => { + const state: readonly Position[] = Object.freeze([1, 7, 2, 6, 3, 5, 4, 1]); + const links = Object.freeze(independent(state).map((row) => Object.freeze(row))); + const result = api.solveLock(state, links); + expect(result).toHaveLength(7); + if (result === null) throw new Error('Expected solution'); + expect(replay({ state, links }, result)).toEqual(state.map(() => 4)); + expect(api.solveLock(state, links)).toEqual(result); + expect(api.solveLock(state, links)).not.toBe(result); + }); + it('returns [] for open and null only for proved unreachable locks', () => { + expect(api.solveLock([4, 4], [[0, 0], [0, 0]])).toEqual([]); + expect(api.solveLock([1, 1, 1], inverse)).toBeNull(); + expect(api.solveLock([3, 4], [[0, 1], [-1, 0]])).toBeNull(); + expect(api.solveLock([3, 4], [[0, 1], [1, 0]])).toBeNull(); + }); + it('handles singular matrices and repeated-plate schedules exactly', () => { + expect(api.solveLock([1, 1], [[0, 1], [1, 0]])).toEqual([[0, 3]]); + const lock = { state: [3, 1, 1] as const, links: inverse }; + const result = api.solveLock(lock.state, lock.links); + expect(result).toHaveLength(4); + if (result === null) throw new Error('Expected solution'); + expect(replay(lock, result)).toEqual([4, 4, 4]); + expect(new Set(result.map(([index]) => index)).size).toBeLessThan(result.length); + }); + it('allows net movements above six while minimizing actions', () => { + const lock = { state: [7, 7, 1] as const, links: [[0, -1, 1], [0, 0, 1], [0, 0, 0]] as const }; + const result = api.solveLock(lock.state, lock.links); + expect(referenceActions(lock)).toBe(4); + expect(result).toHaveLength(4); + if (result === null) throw new Error('Expected solution'); + expect(replay(lock, result)).toEqual([4, 4, 4]); + }); + it('rejects invalid dimensions, positions, links, diagonals and sparse arrays', () => { + const sparseState = [4, 4, 4]; + delete sparseState[1]; + const sparseMatrix = [[0, 0], [0, 0]]; + delete sparseMatrix[0]; + const sparseRow = [0, 0]; + delete sparseRow[1]; + const bad: readonly (readonly [unknown, unknown])[] = [ + [null, null], [[], []], [[4], [[0]]], [[0, 4], [[0, 0], [0, 0]]], + [[4, 8], [[0, 0], [0, 0]]], [[4, 2.5], [[0, 0], [0, 0]]], + [[4, NaN], [[0, 0], [0, 0]]], [[4, '4'], [[0, 0], [0, 0]]], + [[4, 4], [[0, 0]]], [[4, 4], [[0, 0], [0]]], + [[4, 4], [[1, 0], [0, 0]]], [[4, 4], [[0, 2], [0, 0]]], + [sparseState, [[0, 0, 0], [0, 0, 0], [0, 0, 0]]], + [[4, 4], sparseMatrix], [[4, 4], [sparseRow, [0, 0]]], + ]; + for (const [state, links] of bad) expect(() => solveUnknown(state, links)).toThrow(api.LockInputError); + const inherited = [4, 4]; + delete inherited[0]; + Object.setPrototypeOf(inherited, Object.assign(Object.create(Array.prototype), { 0: 4 })); + expect(() => solveUnknown(inherited, [[0, 0], [0, 0]])).toThrow(api.LockInputError); + }); + it('blocks the complete direct movement when a linked pin hits a boundary', () => { + expect(unitMove([4, 7], [[0, 1], [0, 0]], 0, 1)).toBeNull(); + const lock = { state: [1, 7] as const, links: [[0, 1], [0, 0]] as const }; + const result = api.solveLock(lock.state, lock.links); + const expected = referenceActions(lock); + if (expected === null) throw new Error('Expected reachable oracle lock'); + expect(result).toHaveLength(expected); + if (result === null) throw new Error('Expected solution'); + expect(replay(lock, result)).toEqual([4, 4]); + }); + it('supports 18-plate safe codes and reports encoding exhaustion separately from null', () => { + const state: Position[] = [1, ...Array.from({ length: 17 }, (): Position => 4)]; + expect(api.solveLock(state, independent(state))).toEqual([[0, 3]]); + const tooWide = Array.from({ length: 19 }, (): Position => 4); + expect(() => api.solveLock(tooWide, independent(tooWide))).toThrow(api.SearchLimitError); + try { api.solveLock(tooWide, independent(tooWide)); } catch (error) { + expect(error).toBeInstanceOf(api.SearchLimitError); + if (error instanceof api.SearchLimitError) expect(error.limit).toBe('stateEncoding'); + } + }); +}); diff --git a/tests/unit/core-limits.test.ts b/tests/unit/core-limits.test.ts new file mode 100644 index 0000000..6d10b3e --- /dev/null +++ b/tests/unit/core-limits.test.ts @@ -0,0 +1,45 @@ +import { expect, it } from 'vitest'; +import { SearchLimitError } from '../../src/index.ts'; +import { LockModel } from '../../src/lock-model.ts'; +import { PreparedSearch, SearchBudget, movementLimits } from '../../src/search-limits.ts'; +import { BfsSearch } from '../../src/search-bfs.ts'; +import { MatrixSearch } from '../../src/matrix-search.ts'; +import { analyzeMatrix } from '../../src/matrix-analysis.ts'; + +it('reports exhaustion separately on BFS, A*, singular fallback and greedy certificate', () => { + const inputs = [ + new LockModel([1, 7], [[0, 0], [0, 0]]), + new LockModel([3, 1, 1], [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]]), + new LockModel([1, 1], [[0, 1], [1, 0]]), + ]; + for (const model of inputs) { + const prepared = new PreparedSearch(model); + for (const options of [{ maxExpanded: 0 }, { maxVisited: 1 }]) { + expect(() => new MatrixSearch(model, prepared, new SearchBudget(options)).solve()).toThrow(SearchLimitError); + expect(() => new BfsSearch(prepared, new SearchBudget(options)).solve()).toThrow(SearchLimitError); + } + expect(() => new BfsSearch(prepared, new SearchBudget({ maxFrontier: 1 })).solve()).toThrow(SearchLimitError); + } +}); +it('dense and sparse BFS preserve exact action order, including wider safe codes', () => { + const model = new LockModel([2, 7, 1], [[0, 0, 0], [-1, 0, -1], [-1, -1, 0]]); + const prepared = new PreparedSearch(model); + const expected = [[0, 5], [2, 3]]; + expect(new BfsSearch(prepared, new SearchBudget()).solve()).toEqual(expected); + expect(new BfsSearch(prepared, new SearchBudget({ maxDenseBytes: 0 })).solve()).toEqual(expected); + const state = [1, ...Array.from({ length: 17 }, () => 4)]; + const wide = new PreparedSearch(new LockModel(state, state.map(() => state.map(() => 0)))); + expect(new BfsSearch(wide, new SearchBudget({ maxDenseBytes: 0, maxVisited: 5, maxExpanded: 1, maxFrontier: 4 })).solve()).toEqual([[0, 3]]); +}); +it('distinguishes exact rational proofs from rank deficiency', () => { + expect(analyzeMatrix(new LockModel([3, 4], [[0, 1], [-1, 0]])).kind).toBe('noninteger'); + expect(analyzeMatrix(new LockModel([3, 4], [[0, 1], [1, 0]])).kind).toBe('inconsistent'); + expect(analyzeMatrix(new LockModel([1, 1], [[0, 1], [1, 0]])).kind).toBe('singular'); + expect(analyzeMatrix(new LockModel([7, 7, 1], [[0, -1, 1], [0, 0, 1], [0, 0, 0]]))).toEqual({ kind: 'unique-integer', rank: 3, net: [-3, -6, 12], lowerBound: 4 }); +}); +it('computes the legal range from all direct affected pins atomically', () => { + const prepared = new PreparedSearch(new LockModel([4, 7], [[0, 1], [0, 0]])); + const effect = prepared.effects[0]; + if (effect === undefined) throw new Error('Missing prepared effect'); + expect(movementLimits(new Uint8Array([3, 6]), effect)).toEqual([0, 3]); +}); diff --git a/tests/unit/core-oracle.test.ts b/tests/unit/core-oracle.test.ts new file mode 100644 index 0000000..cc8d289 --- /dev/null +++ b/tests/unit/core-oracle.test.ts @@ -0,0 +1,78 @@ +import { describe, expect, it } from 'vitest'; +import { solveLock } from '../../src/index.ts'; +import type { Link, Position } from '../../src/index.ts'; +import { LockModel } from '../../src/lock-model.ts'; +import { PreparedSearch, SearchBudget } from '../../src/search-limits.ts'; +import { BfsSearch } from '../../src/search-bfs.ts'; +import { referenceActions, replay } from '../helpers/oracle.ts'; +import catalog from '../../benchmarks/fixtures/catalog.json' with { type: 'json' }; +import manifest from '../../benchmarks/fixtures/manifest.json' with { type: 'json' }; +import catalogText from '../../benchmarks/fixtures/catalog.json?raw'; + +function position(value: number): Position { + if (value === 1 || value === 2 || value === 3 || value === 4 || value === 5 || value === 6 || value === 7) return value; + throw new Error('Invalid fixture position'); +} +function link(value: number): Link { + if (value === -1 || value === 0 || value === 1) return value; + throw new Error('Invalid fixture link'); +} + +describe('independent minimum-action oracle', () => { + it('checks all 441 two-plate cases and replays every intermediate unit move', () => { + let checked = 0; + for (const a of [-1, 0, 1] as const) for (const b of [-1, 0, 1] as const) { + for (let first = 1; first <= 7; first += 1) for (let second = 1; second <= 7; second += 1) { + const lock = { state: [position(first), position(second)] as const, links: [[0, a], [b, 0]] as const }; + const result = solveLock(lock.state, lock.links); + expect(result?.length ?? null, JSON.stringify(lock)).toBe(referenceActions(lock)); + if (result !== null) expect(replay(lock, result)).toEqual([4, 4]); + checked += 1; + } + } + expect(checked).toBe(441); + }); + it('checks 192 seeded three-plate inputs against A*, dense BFS and sparse BFS', () => { + let seed = 0x51405; + const random = (max: number): number => { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0; + return seed % max; + }; + for (let sample = 0; sample < 192; sample += 1) { + const state = Array.from({ length: 3 }, () => position(1 + random(7))); + const links = Array.from({ length: 3 }, (_, row) => Array.from({ length: 3 }, (_, column) => row === column ? 0 : link(random(3) - 1))); + const lock = { state, links }; + const expected = referenceActions(lock); + const prepared = new PreparedSearch(new LockModel(state, links)); + const results = [solveLock(state, links), new BfsSearch(prepared, new SearchBudget()).solve(), + new BfsSearch(prepared, new SearchBudget({ maxDenseBytes: 0 })).solve()]; + for (const result of results) { + expect(result?.length ?? null, JSON.stringify(lock)).toBe(expected); + if (result !== null) expect(replay(lock, result)).toEqual([4, 4, 4]); + } + } + }); +}); + +describe('preserved catalog', () => { + it('keeps all 45 fixtures and the pinned source hash', async () => { + const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(catalogText)); + const hash = [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join(''); + expect(hash).toBe(manifest.catalogSha256); + expect(catalog).toHaveLength(45); + expect(new Set(catalog.map((entry) => entry.id)).size).toBe(45); + expect(manifest.source.revision).toBe('eee0bf50ebcb7fffd2b47954fd76bb015854e365'); + }); + for (const entry of catalog) { + it(`${entry.id}: solves in ${entry.expectedActions} actions with legal intermediate moves`, () => { + const state = entry.definition.state.map(position); + const links = entry.definition.links.map((row) => row.map(link)); + const before = { state: [...state], links: links.map((row) => [...row]) }; + const result = solveLock(state, links); + expect(result).toHaveLength(entry.expectedActions); + if (result === null) throw new Error('Expected catalog solution'); + expect(replay({ state, links }, result)).toEqual(state.map(() => 4)); + expect({ state, links }).toEqual(before); + }); + } +}); diff --git a/tests/unit/release-files.test.ts b/tests/unit/release-files.test.ts new file mode 100644 index 0000000..f2b45a7 --- /dev/null +++ b/tests/unit/release-files.test.ts @@ -0,0 +1,103 @@ +import { execFileSync } from 'node:child_process'; +import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { prepareRelease, verifyBenchmarkEvidence, verifyPreparedRelease } from '../../scripts/release-files.ts'; +import { releaseAssets, sha256 } from '../../scripts/release-lib.ts'; + +async function createPreparedFixture() { + const root = await mkdtemp(join(tmpdir(), 'gothic-release-test-')); + const sourceSha = 'a'.repeat(40); + const packaged = join(root, 'packed/package'); const evidence = join(root, 'evidence'); const output = join(root, 'prepared'); + for (const directory of [join(root, 'dist'), join(root, 'artifacts/package'), join(packaged, 'dist'), evidence]) await mkdir(directory, { recursive: true }); + const metadata = JSON.stringify({ name: 'gothic-lock-solver', version: '1.0.0' }); + const build = JSON.stringify({ schemaVersion: 1, sourceSha, version: '1.0.0' }); + await writeFile(join(root, 'package.json'), metadata); await writeFile(join(packaged, 'package.json'), metadata); + await writeFile(join(root, 'dist/manifest.json'), build); await writeFile(join(packaged, 'dist/manifest.json'), build); + for (const filename of releaseAssets.filter(name => /\.(?:mjs|js)$/.test(name))) { + await writeFile(join(root, 'dist', filename), 'export const marker = 1;\n'); + await writeFile(join(packaged, 'dist', filename), 'export const marker = 1;\n'); + } + const tarball = join(root, 'artifacts/package/package.tgz'); + execFileSync('tar', ['-czf', tarball, '-C', join(root, 'packed'), 'package']); + const markdown = '# Measured evidence\n'; + const envelope = { schemaVersion: 1, quality: 'passed', performance: 'passed', config: { lockCount: 45 }, + memory: { performance: { verdict: 'passed' } }, metadata: { candidate: { revision: sourceSha, moduleSha256: sha256('export const marker = 1;\n'), format: 'tuple' } }, + }; + const reportData = { ...envelope, correctness: { passed: true } }; + const json = JSON.stringify(reportData); + await writeFile(join(evidence, 'benchmark.json'), json); await writeFile(join(evidence, 'benchmark.md'), markdown); + const evidenceData = { ...envelope, reports: { json: { path: 'benchmark.json', sha256: sha256(json) }, markdown: { path: 'benchmark.md', sha256: sha256(markdown) } } }; + await writeFile(join(evidence, 'benchmark-evidence.json'), JSON.stringify(evidenceData)); + const manifest = await prepareRelease({ root, outputDir: output, benchmarkDir: evidence, sourceSha, kind: 'stable' }); + return { root, sourceSha, evidence, output, tarball, manifest, evidenceData, reportData }; +} + +type PreparedFixture = Awaited>; +type EvidenceData = PreparedFixture['evidenceData']; + +async function replaceEvidence(fixture: PreparedFixture, evidenceData: unknown): Promise { + const bytes = JSON.stringify(evidenceData); const hash = sha256(bytes); + await writeFile(join(fixture.output, 'benchmark-evidence.json'), bytes); + await writeFile(join(fixture.output, 'release-manifest.json'), JSON.stringify({ ...fixture.manifest, + assets: { ...fixture.manifest.assets, 'benchmark-evidence.json': hash } })); + return hash; +} + +describe('prepared archive identity', () => { + it('copies one verified archive, verifies its packed files, and detects later asset tampering', async () => { + const fixture = await createPreparedFixture(); + const { root, output, tarball, manifest, evidence, sourceSha } = fixture; + try { + expect(await readFile(join(output, 'package.tgz'))).toEqual(await readFile(tarball)); + expect((await verifyPreparedRelease(output)).tarballSha256).toBe(manifest.tarballSha256); + await expect(verifyBenchmarkEvidence(evidence, 'b'.repeat(40), sha256('export const marker = 1;\n'))).rejects.toThrow(/candidate/); + await writeFile(join(output, 'gothic-lock-solver.js'), 'changed'); + await expect(verifyPreparedRelease(output)).rejects.toThrow(/identity/); + await writeFile(join(evidence, 'benchmark.md'), 'edited report'); + await expect(verifyBenchmarkEvidence(evidence, sourceSha, sha256('export const marker = 1;\n'))).rejects.toThrow(/identity/); + } finally { await rm(root, { recursive: true, force: true }); } + }); + + const changes: readonly { name: string; change: (value: EvidenceData) => unknown; error: RegExp }[] = [ + { name: 'quality regression', change: value => ({ ...value, quality: 'regression' }), error: /quality/ }, + { name: 'uncertain performance', change: value => ({ ...value, performance: 'inconclusive' }), error: /performance/ }, + { name: 'wrong source revision', change: value => ({ ...value, metadata: { candidate: { ...value.metadata.candidate, revision: 'b'.repeat(40) } } }), error: /candidate/ }, + { name: 'wrong measured module', change: value => ({ ...value, metadata: { candidate: { ...value.metadata.candidate, moduleSha256: 'b'.repeat(64) } } }), error: /candidate/ }, + { name: 'wrong report link', change: value => ({ ...value, reports: { ...value.reports, json: { ...value.reports.json, path: 'different.json' } } }), error: /report path/ }, + { name: 'wrong report hash', change: value => ({ ...value, reports: { ...value.reports, json: { ...value.reports.json, sha256: 'b'.repeat(64) } } }), error: /identity/ }, + ]; + it.each(changes)('rejects $name even after the matching manifest digest is recomputed', async ({ change, error }) => { + const fixture = await createPreparedFixture(); + try { + await replaceEvidence(fixture, change(fixture.evidenceData)); + await expect(verifyPreparedRelease(fixture.output)).rejects.toThrow(error); + } finally { await rm(fixture.root, { recursive: true, force: true }); } + }); + + it('requires acceptance supplied by the verifier, ignoring an artifact acceptance claim', async () => { + const fixture = await createPreparedFixture(); + try { + const report = JSON.stringify({ ...fixture.reportData, performance: 'inconclusive' }); + await writeFile(join(fixture.output, 'benchmark.json'), report); + fixture.manifest = { ...fixture.manifest, assets: { ...fixture.manifest.assets, 'benchmark.json': sha256(report) } }; + const hash = await replaceEvidence(fixture, { ...fixture.evidenceData, performance: 'inconclusive', acceptanceReason: 'Self-approved artifact', + reports: { ...fixture.evidenceData.reports, json: { path: 'benchmark.json', sha256: sha256(report) } } }); + await expect(verifyPreparedRelease(fixture.output)).rejects.toThrow(/performance/); + await expect(verifyPreparedRelease(fixture.output, { acceptedEvidenceSha256: hash, acceptanceReason: 'Maintainer reviewed this exact evidence.' })).resolves.toMatchObject({ version: '1.0.0' }); + await expect(verifyPreparedRelease(fixture.output, { acceptedEvidenceSha256: 'b'.repeat(64), acceptanceReason: 'Wrong evidence.' })).rejects.toThrow(/performance/); + } finally { await rm(fixture.root, { recursive: true, force: true }); } + }); + + it('rejects an evidence pass that disagrees with the detailed report after all hashes are updated', async () => { + const fixture = await createPreparedFixture(); + try { + const report = JSON.stringify({ ...fixture.reportData, quality: 'regression' }); + await writeFile(join(fixture.output, 'benchmark.json'), report); + fixture.manifest = { ...fixture.manifest, assets: { ...fixture.manifest.assets, 'benchmark.json': sha256(report) } }; + await replaceEvidence(fixture, { ...fixture.evidenceData, reports: { ...fixture.evidenceData.reports, json: { path: 'benchmark.json', sha256: sha256(report) } } }); + await expect(verifyPreparedRelease(fixture.output)).rejects.toThrow(/report.*quality/); + } finally { await rm(fixture.root, { recursive: true, force: true }); } + }); +}); diff --git a/tests/unit/release-network.test.ts b/tests/unit/release-network.test.ts new file mode 100644 index 0000000..3898917 --- /dev/null +++ b/tests/unit/release-network.test.ts @@ -0,0 +1,141 @@ +import { readFileSync, writeFileSync } from 'node:fs'; +import { mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { releaseAssets, sha256, validateManifest } from '../../scripts/release-lib.ts'; +import { completeGithubRelease, publishOrResumeNpm, registryMetadata, verifyCdn, verifyRegistry } from '../../scripts/release-network.ts'; + +const command = vi.hoisted(() => vi.fn<(file: string, args: readonly string[]) => string>()); +vi.mock('node:child_process', () => ({ execFileSync: command })); +const fetchMock = vi.fn(); +const archive = Buffer.from('exact prepared archive'); +const assetBytes = (filename: string): Buffer => filename === 'package.tgz' ? archive : Buffer.from(`prepared ${filename}`); +const manifest = validateManifest({ schemaVersion: 1, name: 'gothic-lock-solver', version: '1.0.0', sourceSha: 'a'.repeat(40), kind: 'stable', + tarballSha256: sha256(archive), assets: Object.fromEntries(releaseAssets.map(filename => [filename, sha256(assetBytes(filename))])) }); +const metadata = { name: manifest.name, version: manifest.version, dist: { tarball: 'https://registry.npmjs.org/gothic-lock-solver/-/gothic-lock-solver-1.0.0.tgz' } }; +const jsonResponse = (body: unknown): Response => new Response(JSON.stringify(body), { status: 200, headers: { 'Content-Type': 'application/json' } }); + +beforeEach(() => { command.mockReset(); fetchMock.mockReset(); vi.stubGlobal('fetch', fetchMock); }); +afterEach(() => { vi.unstubAllGlobals(); }); + +async function releaseDirectory(): Promise { + const directory = await mkdtemp(join(tmpdir(), 'gothic-network-test-')); + for (const filename of releaseAssets) await writeFile(join(directory, filename), assetBytes(filename)); + await writeFile(join(directory, 'release-manifest.json'), JSON.stringify(manifest)); + await writeFile(join(directory, 'release-notes.md'), 'Prepared release notes'); + return directory; +} + +describe('npm and CDN delivery boundaries', () => { + it('distinguishes an absent version from a registry outage', async () => { + fetchMock.mockResolvedValueOnce(new Response('', { status: 404 })); + expect(await registryMetadata('1.0.0')).toBeNull(); + fetchMock.mockResolvedValueOnce(new Response('', { status: 503 })); + await expect(registryMetadata('1.0.0')).rejects.toThrow(/503/); + }); + + it('verifies an existing archive and rejects different bytes at the same version', async () => { + fetchMock.mockResolvedValueOnce(jsonResponse(metadata)).mockResolvedValueOnce(new Response(archive)); + await expect(verifyRegistry(manifest)).resolves.toBeUndefined(); + fetchMock.mockResolvedValueOnce(jsonResponse(metadata)).mockResolvedValueOnce(new Response('different archive')); + await expect(verifyRegistry(manifest)).rejects.toThrow(/identity/); + }); + + it('publishes the one prepared archive only when the npm version is absent', async () => { + const directory = await releaseDirectory(); + try { + fetchMock.mockResolvedValue(new Response('', { status: 404 })); + await publishOrResumeNpm(directory, manifest); + expect(command).toHaveBeenCalledWith('npm', ['publish', join(directory, 'package.tgz'), '--access', 'public', '--tag', 'latest', '--provenance', '--ignore-scripts', '--registry', 'https://registry.npmjs.org/'], expect.objectContaining({ stdio: 'inherit' })); + } finally { await rm(directory, { recursive: true, force: true }); } + }); + + it('resumes identical npm bytes without republishing and refuses different existing bytes', async () => { + const directory = await releaseDirectory(); + try { + fetchMock.mockResolvedValueOnce(jsonResponse(metadata)).mockResolvedValueOnce(jsonResponse(metadata)).mockResolvedValueOnce(new Response(archive)); + await publishOrResumeNpm(directory, manifest); + expect(command).not.toHaveBeenCalled(); + fetchMock.mockResolvedValueOnce(jsonResponse(metadata)).mockResolvedValueOnce(jsonResponse(metadata)).mockResolvedValueOnce(new Response('changed')); + await expect(publishOrResumeNpm(directory, manifest)).rejects.toThrow(/identity/); + expect(command).not.toHaveBeenCalled(); + } finally { await rm(directory, { recursive: true, force: true }); } + }); + + it.each([ + { name: 'MIME type', type: 'text/plain', cors: '*', bytes: assetBytes('gothic-lock-solver.mjs'), error: /headers/ }, + { name: 'CORS', type: 'application/javascript', cors: 'https://elsewhere.test', bytes: assetBytes('gothic-lock-solver.mjs'), error: /headers/ }, + { name: 'runtime hash', type: 'application/javascript', cors: '*', bytes: Buffer.from('changed'), error: /identity/ }, + ])('rejects an invalid CDN $name', async ({ type, cors, bytes, error }) => { + fetchMock.mockResolvedValue(new Response(bytes.toString('utf8'), { headers: { 'Content-Type': type, 'Access-Control-Allow-Origin': cors } })); + await expect(verifyCdn(manifest)).rejects.toThrow(error); + }); +}); + +function mockGithub(directory: string, options: { draft: boolean; missing?: string; tagSha?: string; newer?: boolean }): void { + const tag = `v${manifest.version}`; + const filenames = [...releaseAssets, 'release-manifest.json']; + const release = { tag_name: tag, draft: options.draft, prerelease: false, assets: filenames.filter(name => name !== options.missing).map(name => ({ name })) }; + command.mockImplementation((file, args) => { + if (file !== 'gh') throw new Error('Unexpected executable'); + if (args[0] === 'api') { + const endpoint = args[1] ?? ''; + if (endpoint.includes('matching-refs')) return JSON.stringify([{ ref: `refs/tags/${tag}` }]); + if (endpoint.includes('/git/ref/tags/')) return JSON.stringify({ object: { type: 'commit', sha: options.tagSha ?? manifest.sourceSha } }); + if (endpoint.includes('releases?')) return JSON.stringify([release, ...(options.newer ? [{ tag_name: 'v1.1.0', draft: false, prerelease: false }] : [])]); + if (endpoint.includes('/releases/tags/')) return JSON.stringify(release); + } + if (args[0] === 'release' && args[1] === 'download') { + const filename = args[args.indexOf('--pattern') + 1]; const destination = args[args.indexOf('--dir') + 1]; + if (!filename || !destination) throw new Error('Missing download arguments'); + writeFileSync(join(destination, filename), readFileSync(join(directory, filename))); + return ''; + } + if (args[0] === 'release' && ['upload', 'edit'].includes(args[1] ?? '')) return ''; + throw new Error(`Unexpected gh call: ${args.join(' ')}`); + }); + fetchMock.mockResolvedValue(jsonResponse({ ...metadata, version: options.newer ? '1.1.0' : '1.0.0' })); +} + +describe('GitHub partial-release recovery', () => { + it('rejects an existing tag at a different commit before uploading or finalizing', async () => { + const directory = await releaseDirectory(); + try { + mockGithub(directory, { draft: true, tagSha: 'b'.repeat(40) }); + await expect(completeGithubRelease(directory, manifest, 'owner/repo')).rejects.toThrow(/tag/); + expect(command.mock.calls.some(([, args]) => args[0] === 'release')).toBe(false); + } finally { await rm(directory, { recursive: true, force: true }); } + }); + + it('uploads only missing draft assets and verifies downloads before finalizing', async () => { + const directory = await releaseDirectory(); + try { + mockGithub(directory, { draft: true, missing: 'benchmark-evidence.json' }); + await completeGithubRelease(directory, manifest, 'owner/repo'); + const uploads = command.mock.calls.filter(([, args]) => args[1] === 'upload'); + expect(uploads).toHaveLength(1); + expect(uploads[0]?.[1]).toContain(join(directory, 'benchmark-evidence.json')); + expect(command.mock.calls.some(([, args]) => args.includes('--clobber'))).toBe(false); + expect(command.mock.calls.at(-1)?.[1]).toEqual(['release', 'edit', 'v1.0.0', '--repo', 'owner/repo', '--draft=false', '--latest=true']); + } finally { await rm(directory, { recursive: true, force: true }); } + }); + + it('refuses to add missing assets to an already published release', async () => { + const directory = await releaseDirectory(); + try { + mockGithub(directory, { draft: false, missing: 'benchmark-evidence.json' }); + await expect(completeGithubRelease(directory, manifest, 'owner/repo')).rejects.toThrow(/Published release/); + expect(command.mock.calls.some(([, args]) => args[1] === 'upload' || args[1] === 'edit')).toBe(false); + } finally { await rm(directory, { recursive: true, force: true }); } + }); + + it('finishes an older draft while preserving the newer stable latest release', async () => { + const directory = await releaseDirectory(); + try { + mockGithub(directory, { draft: true, newer: true }); + await completeGithubRelease(directory, manifest, 'owner/repo'); + expect(command.mock.calls.at(-1)?.[1]).toContain('--latest=false'); + } finally { await rm(directory, { recursive: true, force: true }); } + }); +}); diff --git a/tests/unit/release.test.ts b/tests/unit/release.test.ts new file mode 100644 index 0000000..e339fc5 --- /dev/null +++ b/tests/unit/release.test.ts @@ -0,0 +1,66 @@ +import { describe, expect, it } from 'vitest'; +import { assertDigest, assertReleaseSource, canaryVersion, parseReleaseVersion, previousStableVersion, shouldPromoteGithubLatest, validateManifest, validateEvidenceGate } from '../../scripts/release-lib.ts'; + +describe('release identity and publication guards', () => { + it('accepts stable SemVer and rejects aliases, leading zeros, and shell text', () => { + expect(parseReleaseVersion('1.2.3')).toEqual({ version: '1.2.3', prerelease: false }); + for (const value of ['latest', 'v1.2.3', '01.2.3', '1.2', '1.2.3;true', '1.2.3-01']) { + expect(() => parseReleaseVersion(value)).toThrow(); + } + }); + + it('makes canaries unique by immutable source and run attempt without a stable tag', () => { + expect(canaryVersion('1.2.3', 'a'.repeat(40), '12', '2')).toBe('1.2.3-canary.12.2.aaaaaaaaaaaa'); + expect(parseReleaseVersion('1.2.3-canary.12.2.aaaaaaaaaaaa').prerelease).toBe(true); + expect(() => canaryVersion('1.2.3-beta', 'a'.repeat(40), '12', '2')).toThrow(); + expect(() => canaryVersion('1.2.3', 'bad', '12', '2')).toThrow(); + }); + + it('only permits stable source on a main push or explicit main resume', () => { + expect(() => assertReleaseSource('stable', 'refs/heads/main', 'push', 'owner/repo', 'owner/repo')).not.toThrow(); + expect(() => assertReleaseSource('stable', 'refs/heads/main', 'workflow_dispatch', 'owner/repo', 'owner/repo')).not.toThrow(); + for (const [ref, event, source] of [ + ['refs/heads/feature', 'push', 'owner/repo'], + ['refs/heads/main', 'pull_request', 'owner/repo'], + ['refs/heads/main', 'push', 'fork/repo'], + ]) expect(() => assertReleaseSource('stable', ref ?? '', event ?? '', 'owner/repo', source ?? '')).toThrow(); + }); + + it('only permits manually requested canaries from the owner repository', () => { + expect(() => assertReleaseSource('canary', 'refs/heads/feature', 'workflow_dispatch', 'owner/repo', 'owner/repo')).not.toThrow(); + expect(() => assertReleaseSource('canary', 'refs/heads/feature', 'push', 'owner/repo', 'owner/repo')).toThrow(); + expect(() => assertReleaseSource('canary', 'refs/pull/1/head', 'workflow_dispatch', 'owner/repo', 'owner/repo')).toThrow(); + }); + + it('refuses to resume an existing npm version with different bytes', () => { + expect(() => assertDigest(Buffer.from('abc'), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', 'package')).not.toThrow(); + expect(() => assertDigest(Buffer.from('changed'), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', 'package')).toThrow(/identity/); + }); + + it('rejects unsafe manifest paths and incomplete runtime assets', () => { + expect(() => validateManifest({ schemaVersion: 1, name: 'gothic-lock-solver', version: '1.0.0', sourceSha: 'a'.repeat(40), kind: 'stable', tarballSha256: 'a'.repeat(64), assets: { '../secret': 'a'.repeat(64) } })).toThrow(); + expect(() => validateManifest(null)).toThrow(); + }); + + it('never silently accepts uncertain performance and never allows a quality regression', () => { + const evidence = { schemaVersion: 1, quality: 'passed', performance: 'inconclusive', config: { lockCount: 45 }, memory: { performance: { verdict: 'passed' } } }; + expect(() => validateEvidenceGate(evidence, 'a'.repeat(64))).toThrow(/performance/); + expect(() => validateEvidenceGate(evidence, 'a'.repeat(64), 'b'.repeat(64), 'reviewed')).toThrow(/performance/); + expect(validateEvidenceGate(evidence, 'a'.repeat(64), 'a'.repeat(64), 'Reviewed shared-runner uncertainty.')).toBe('Reviewed shared-runner uncertainty.'); + expect(() => validateEvidenceGate({ ...evidence, quality: 'regression' }, 'a'.repeat(64), 'a'.repeat(64), 'reviewed')).toThrow(/quality/); + expect(() => validateEvidenceGate({ ...evidence, performance: 'passed', config: { lockCount: 44 } }, 'a'.repeat(64))).toThrow(/45/); + }); + + it('selects the preceding stable release, excluding canaries and the version being resumed', () => { + expect(previousStableVersion(['1.2.0', '1.10.0', '1.11.0-canary.1', '1.11.0', '2.0.0'], '1.11.0')).toBe('1.10.0'); + expect(previousStableVersion(['1.0.0-canary.1'], '1.0.0')).toBeNull(); + expect(previousStableVersion(['1.0.0'], '1.0.0-canary.5')).toBe('1.0.0'); + }); + + it('resumes an older stable release without downgrading GitHub latest', () => { + expect(shouldPromoteGithubLatest('stable', '1.0.0', '1.1.0', ['1.1.0'])).toBe(false); + expect(shouldPromoteGithubLatest('stable', '1.0.0', '1.0.0', ['1.1.0'])).toBe(false); + expect(shouldPromoteGithubLatest('stable', '1.1.0', '1.1.0', ['1.0.0'])).toBe(true); + expect(shouldPromoteGithubLatest('canary', '1.2.0-canary.4', '1.1.0', ['1.1.0'])).toBe(false); + }); +}); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..b1eb91d --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "emitDeclarationOnly": true, + "declaration": true, + "rootDir": "src", + "outDir": "dist", + "types": [], + "lib": ["ES2022"] + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2a315c3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noPropertyAccessFromIndexSignature": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noUncheckedSideEffectImports": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "skipLibCheck": false, + "noEmit": true, + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "types": ["node"] + }, + "include": ["src/**/*.ts", "cli/**/*.ts", "scripts/**/*.ts", "tests/**/*.ts", "benchmarks/**/*.ts", "*.config.ts"], + "exclude": ["node_modules", "dist", "tests/distribution/fixtures"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..f5a4d98 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,31 @@ +import { fileURLToPath } from 'node:url'; +import { defineConfig } from 'vite'; + +export default defineConfig(({ mode }) => { + const minified = mode === 'minified'; + return { + build: { + target: 'es2022', + outDir: 'dist', + emptyOutDir: false, + sourcemap: false, + minify: false, + lib: { + entry: fileURLToPath(new URL('./src/index.ts', import.meta.url)), + name: 'GothicLockSolver', + formats: ['es', 'iife'], + fileName: (format) => `gothic-lock-solver${minified ? '.min' : ''}.${format === 'es' ? 'mjs' : 'js'}`, + }, + rolldownOptions: { + output: { + // Explicit full Rolldown minification includes ESM whitespace, unlike + // the Vite library-mode build.minify shortcut. + minify: minified, + exports: 'named', + codeSplitting: false, + comments: { legal: false }, + }, + }, + }, + }; +}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..c28ee32 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,26 @@ +import { playwright } from '@vitest/browser-playwright'; +import { defineConfig } from 'vitest/config'; + +const shared = ['tests/unit/core*.test.ts', 'tests/browser/*.test.ts']; + +export default defineConfig({ + test: { + coverage: { provider: 'v8', include: ['src/**/*.ts', 'cli/**/*.ts'], thresholds: { statements: 80, branches: 80, functions: 80, lines: 80 }, reporter: ['text', 'json-summary', 'html'] }, + projects: [ + { test: { name: 'node', environment: 'node', include: [...shared, 'tests/unit/*.test.ts'], testTimeout: 120_000 } }, + { test: { name: 'distribution', environment: 'node', include: ['tests/distribution/*.test.ts'], testTimeout: 120_000 } }, + { + test: { + include: shared, + testTimeout: 120_000, + browser: { + enabled: true, + headless: true, + provider: playwright(), + instances: [{ browser: 'chromium', name: 'chromium' }, { browser: 'firefox', name: 'firefox' }, { browser: 'webkit', name: 'webkit' }], + }, + }, + }, + ], + }, +}); From 32f2c9494ab338163679a62b9b96bca4fd11c7c2 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 7 Sep 2026 13:32:54 +0400 Subject: [PATCH 7/9] ci: verify and deliver typed solver with reviewed benchmark evidence --- .github/dependabot.yml | 13 + .github/workflows/ci.yml | 186 + .github/workflows/release.yml | 344 + AGENTS.md | 3 +- README.md | 31 + .../benchmark-evidence.json | 5953 ++++ .../benchmark.json | 25093 ++++++++++++++++ .../benchmark.md | 3858 +++ docs/development.md | 9 + docs/releasing.md | 12 +- docs/reviews/2026-09-07-principles-review.md | 240 + scripts/release-network.ts | 8 +- tests/unit/release-network.test.ts | 18 +- 13 files changed, 35760 insertions(+), 8 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark-evidence.json create mode 100644 docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.json create mode 100644 docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md create mode 100644 docs/reviews/2026-09-07-principles-review.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..cecdeb8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + versioning-strategy: increase + open-pull-requests-limit: 5 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d3f1d29 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,186 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + validation: + name: Package, coverage and three browsers + runs-on: ubuntu-24.04 + timeout-minutes: 30 + defaults: + run: + working-directory: candidate + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + path: candidate + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 22.23.2 + - name: Record Node 22 consumer executable + id: node22 + run: echo "path=$(command -v node)" >> "$GITHUB_OUTPUT" + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 26.8.1 + - name: Record Node 26 consumer executable + id: node26 + run: echo "path=$(command -v node)" >> "$GITHUB_OUTPUT" + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + with: + version: 12.3.4 + package_json_file: candidate/package.json + run_install: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24.19.0 + cache: pnpm + cache-dependency-path: candidate/pnpm-lock.yaml + - name: Install pinned npm and frozen dependencies + run: | + npm install --global --ignore-scripts npm@12.0.2 + pnpm install --frozen-lockfile + node --version + pnpm --version + npm --version + - name: Install actual browser engines + run: pnpm exec playwright install --with-deps chromium firefox webkit + - name: Lint + run: pnpm lint + - name: Strict type check + run: pnpm typecheck + - name: Node tests and enforced coverage + run: pnpm test:node --coverage + - name: Build all five executable files + run: pnpm build + - name: Distribution consumers + run: pnpm test:distribution + - name: Chromium, Firefox and WebKit unit tests + run: pnpm test:browser + - name: HTTP, module, classic, file and Worker consumers + run: pnpm test:e2e + - name: Verify one archive in Node 22, 24 and 26 and all browsers + env: + NODE22: ${{ steps.node22.outputs.path }} + NODE26: ${{ steps.node26.outputs.path }} + run: pnpm verify:package --node "$NODE22" --node "$NODE26" + - name: Preserve test, coverage and distribution evidence + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ci-validation + path: | + candidate/dist/ + candidate/artifacts/package/ + candidate/coverage/ + candidate/playwright-report/ + candidate/test-results/ + if-no-files-found: warn + retention-days: 30 + + node26: + name: Node 26 source tests + runs-on: ubuntu-24.04 + timeout-minutes: 15 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + with: + version: 12.3.4 + run_install: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 26.8.1 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm test:node + + benchmark: + name: Paired 45-lock target comparison + needs: validation + runs-on: ubuntu-24.04 + timeout-minutes: 30 + defaults: + run: + working-directory: candidate + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + path: candidate + persist-credentials: false + - name: Check out the actual PR target commit separately + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.base.sha || '3cbbeedbb409a2d8ffa5b00c778cfcb0b07c0ec6' }} + path: baseline + persist-credentials: false + - name: Check out pinned upstream for hash-checked comparison only + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: 1h8s/unlockmyloot + ref: eee0bf50ebcb7fffd2b47954fd76bb015854e365 + path: upstream + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + with: + version: 12.3.4 + package_json_file: candidate/package.json + run_install: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24.19.0 + cache: pnpm + cache-dependency-path: candidate/pnpm-lock.yaml + - run: pnpm install --frozen-lockfile + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ci-validation + path: candidate + - name: Build a TypeScript target in the same runtime when needed + run: | + if [ ! -f ../baseline/src/index.mjs ]; then + pnpm --dir ../baseline install --frozen-lockfile + pnpm --dir ../baseline build + fi + - name: Generate baseline from the separately checked-out source + id: baseline + run: node scripts/ci-baseline.ts checkout ../baseline artifacts/baseline + - name: Compare exact built candidate and target, with isolated RSS samples + env: + BASELINE_MODULE: ${{ steps.baseline.outputs.module }} + BASELINE_SNAPSHOT: ${{ steps.baseline.outputs.snapshot }} + BASELINE_FORMAT: ${{ steps.baseline.outputs.format }} + run: pnpm benchmark:compare --candidate-module dist/gothic-lock-solver.mjs --baseline-module "$BASELINE_MODULE" --baseline-snapshot "$BASELINE_SNAPSHOT" --baseline-format "$BASELINE_FORMAT" --reference ../upstream --output-dir artifacts/benchmark + - name: Show benchmark verdict and measurements + if: always() + run: | + for report in artifacts/benchmark/run-*/benchmark.md; do + if [ -f "$report" ]; then cat "$report" >> "$GITHUB_STEP_SUMMARY"; fi + done + - name: Preserve raw benchmark and baseline evidence + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ci-benchmark + path: | + candidate/artifacts/benchmark/ + candidate/artifacts/baseline/ + if-no-files-found: warn + retention-days: 30 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ed86c2b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,344 @@ +name: Release + +on: + push: + branches: [main] + workflow_dispatch: + inputs: + kind: + description: Stable main release or an owner-branch canary + type: choice + options: [canary, stable] + default: canary + required: true + source_branch: + description: Owner repository branch for a canary; stable always uses main + type: string + default: main + required: true + +permissions: + contents: read + +# Keep partial releases recoverable; a newer run never cancels publication midway. +concurrency: + group: release + cancel-in-progress: false + +env: + RELEASE_KIND: ${{ github.event_name == 'workflow_dispatch' && inputs.kind || 'stable' }} + PERFORMANCE_ACCEPTED_EVIDENCE_SHA256: ${{ vars.PERFORMANCE_ACCEPTED_EVIDENCE_SHA256 }} + PERFORMANCE_ACCEPTANCE_REASON: ${{ vars.PERFORMANCE_ACCEPTANCE_REASON }} + +jobs: + build: + name: Build, verify and measure one archive + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-24.04 + timeout-minutes: 45 + outputs: + source-sha: ${{ steps.source.outputs.sha }} + source-ref: ${{ steps.source.outputs.ref }} + input-artifact: release-input-${{ github.run_id }}-${{ github.run_attempt }} + benchmark-directory: ${{ steps.report.outputs.directory }} + defaults: + run: + working-directory: candidate + steps: + - name: Check out trusted main delivery helpers + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + path: workflow + persist-credentials: false + - name: Check out owner repository source without publication credentials + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.kind == 'canary' && format('refs/heads/{0}', inputs.source_branch) || github.sha }} + path: candidate + persist-credentials: false + - name: Capture immutable source before running candidate code + id: source + env: + SOURCE_BRANCH: ${{ github.event_name == 'workflow_dispatch' && inputs.kind == 'canary' && inputs.source_branch || 'main' }} + run: | + git check-ref-format "refs/heads/$SOURCE_BRANCH" + echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + echo "ref=refs/heads/$SOURCE_BRANCH" >> "$GITHUB_OUTPUT" + - name: Check out pinned external upstream + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: 1h8s/unlockmyloot + ref: eee0bf50ebcb7fffd2b47954fd76bb015854e365 + path: upstream + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 22.23.2 + - id: node22 + run: echo "path=$(command -v node)" >> "$GITHUB_OUTPUT" + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 26.8.1 + - id: node26 + run: echo "path=$(command -v node)" >> "$GITHUB_OUTPUT" + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + with: + version: 12.3.4 + package_json_file: candidate/package.json + run_install: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24.19.0 + cache: pnpm + cache-dependency-path: candidate/pnpm-lock.yaml + - name: Install pinned npm and frozen dependencies + run: | + npm install --global --ignore-scripts npm@12.0.2 + pnpm install --frozen-lockfile + - name: Assign a unique canary version before building + if: env.RELEASE_KIND == 'canary' + env: + RELEASE_SOURCE_SHA: ${{ steps.source.outputs.sha }} + run: node ../workflow/scripts/release.ts canary-version + - name: Install all actual browsers + run: pnpm exec playwright install --with-deps chromium firefox webkit + - name: Check source, coverage, all formats and the one packed archive + env: + NODE22: ${{ steps.node22.outputs.path }} + NODE26: ${{ steps.node26.outputs.path }} + run: | + pnpm check + pnpm test:coverage + "$NODE26" node_modules/vitest/vitest.mjs run --project node + pnpm build + pnpm test:distribution + pnpm test:browser + pnpm test:e2e + pnpm verify:package --node "$NODE22" --node "$NODE26" + - name: Resolve the preceding stable release from npm and GitHub + id: previous + run: | + version=$(node -p "JSON.parse(require('node:fs').readFileSync('package.json','utf8')).version") + node ../workflow/scripts/ci-baseline.ts release "$version" artifacts/baseline + - name: Check out the explicit first-release reference when needed + if: steps.previous.outputs.available != 'true' + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: 3cbbeedbb409a2d8ffa5b00c778cfcb0b07c0ec6 + path: reference-matrix + persist-credentials: false + - name: Generate a snapshot from the pinned first-release checkout + id: reference + if: steps.previous.outputs.available != 'true' + run: node ../workflow/scripts/ci-baseline.ts checkout ../reference-matrix artifacts/reference-baseline + - name: Measure 45 fixtures against previous stable or explicit reference + id: comparison + # Keep the measured artifact available. The separate prepare job enforces + # quality/performance semantics and exact-evidence acceptance before publish. + continue-on-error: true + env: + BASELINE_MODULE: ${{ steps.previous.outputs.module || steps.reference.outputs.module }} + BASELINE_SNAPSHOT: ${{ steps.previous.outputs.snapshot || steps.reference.outputs.snapshot }} + BASELINE_FORMAT: ${{ steps.previous.outputs.format || steps.reference.outputs.format }} + run: pnpm benchmark:compare --candidate-module dist/gothic-lock-solver.mjs --baseline-module "$BASELINE_MODULE" --baseline-snapshot "$BASELINE_SNAPSHOT" --baseline-format "$BASELINE_FORMAT" --reference ../upstream --output-dir artifacts/benchmark + - name: Require one complete evidence set + id: report + run: | + reports=(artifacts/benchmark/run-*) + test "${#reports[@]}" -eq 1 + test -f "${reports[0]}/benchmark-evidence.json" + echo "directory=${reports[0]}" >> "$GITHUB_OUTPUT" + - name: Preserve immutable raw release inputs before the performance gate + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: release-input-${{ github.run_id }}-${{ github.run_attempt }} + path: | + candidate/package.json + candidate/README.md + candidate/LICENSE + candidate/dist/ + candidate/artifacts/package/ + candidate/artifacts/benchmark/ + candidate/artifacts/baseline/ + candidate/artifacts/reference-baseline/ + if-no-files-found: error + retention-days: 30 + - name: Show benchmark and readiness status + if: always() + env: + READY: ${{ vars.NPM_PUBLICATION_READY }} + run: | + for report in artifacts/benchmark/run-*/benchmark.md; do + if [ -f "$report" ]; then cat "$report" >> "$GITHUB_STEP_SUMMARY"; fi + done + for evidence in artifacts/benchmark/run-*/benchmark-evidence.json; do + if [ -f "$evidence" ]; then sha256sum "$evidence" >> "$GITHUB_STEP_SUMMARY"; fi + done + if [ "$READY" != true ]; then + echo 'Publication is disabled. Complete npm ownership/trusted-publisher bootstrap, then configure NPM_PUBLICATION_READY=true. Prepared evidence remains available.' >> "$GITHUB_STEP_SUMMARY" + fi + - name: Preserve test diagnostics even when a check fails + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: release-checks-${{ github.run_id }}-${{ github.run_attempt }} + path: | + candidate/coverage/ + candidate/playwright-report/ + candidate/test-results/ + candidate/artifacts/benchmark/ + if-no-files-found: warn + retention-days: 30 + + prepare: + name: Validate evidence and prepare exact release assets + needs: build + runs-on: ubuntu-24.04 + timeout-minutes: 5 + outputs: + artifact: release-prepared-${{ github.run_id }}-${{ github.run_attempt }} + version: ${{ steps.prepare.outputs.version }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + path: workflow + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24.19.0 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ needs.build.outputs.input-artifact }} + path: candidate + - name: Validate the saved evidence without rebuilding or remeasuring + id: prepare + working-directory: candidate + env: + SOURCE_SHA: ${{ needs.build.outputs.source-sha }} + BENCHMARK_DIRECTORY: ${{ needs.build.outputs.benchmark-directory }} + run: node ../workflow/scripts/release.ts prepare --benchmark-dir "$BENCHMARK_DIRECTORY" --source-sha "$SOURCE_SHA" + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: release-prepared-${{ github.run_id }}-${{ github.run_attempt }} + path: candidate/artifacts/release/ + if-no-files-found: error + retention-days: 30 + + npm: + name: Publish or resume identical npm version + needs: [build, prepare] + if: vars.NPM_PUBLICATION_READY == 'true' + runs-on: ubuntu-24.04 + timeout-minutes: 10 + environment: npm-production + permissions: + contents: read + id-token: write + env: + NPM_PUBLICATION_READY: ${{ vars.NPM_PUBLICATION_READY }} + RELEASE_SOURCE_SHA: ${{ needs.build.outputs.source-sha }} + RELEASE_SOURCE_REF: ${{ needs.build.outputs.source-ref }} + RELEASE_SOURCE_REPOSITORY: ${{ github.repository }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24.19.0 + - run: npm install --global --ignore-scripts npm@12.0.2 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ needs.prepare.outputs.artifact }} + path: artifacts/release + - run: node scripts/release.ts publish + - name: Preserve partial-publication status + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: npm-status-${{ github.run_id }}-${{ github.run_attempt }} + path: artifacts/release/delivery-status.json + if-no-files-found: warn + retention-days: 30 + + verify-delivery: + name: Verify registry bytes and real CDN browsers + needs: [prepare, npm] + runs-on: ubuntu-24.04 + timeout-minutes: 20 + outputs: + artifact: release-verified-${{ github.run_id }}-${{ github.run_attempt }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + with: + version: 12.3.4 + run_install: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24.19.0 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm exec playwright install --with-deps chromium firefox webkit + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ needs.prepare.outputs.artifact }} + path: artifacts/release + - run: node scripts/release.ts verify + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: release-verified-${{ github.run_id }}-${{ github.run_attempt }} + path: artifacts/release/ + if-no-files-found: error + retention-days: 30 + + github: + name: Finalize GitHub release with exact verified assets + needs: [build, prepare, verify-delivery] + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: write + env: + NPM_PUBLICATION_READY: ${{ vars.NPM_PUBLICATION_READY }} + RELEASE_SOURCE_SHA: ${{ needs.build.outputs.source-sha }} + RELEASE_SOURCE_REF: ${{ needs.build.outputs.source-ref }} + RELEASE_SOURCE_REPOSITORY: ${{ github.repository }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24.19.0 + - name: Install exact official GitHub CLI release + env: + GH_ARCHIVE_SHA256: e4d4bb4498e8d007abe545b6568926793ace1b6447da598294a610018cb164be + run: | + curl --fail --location --proto '=https' --tlsv1.2 https://github.com/cli/cli/releases/download/v2.100.0/gh_2.100.0_linux_amd64.tar.gz -o "$RUNNER_TEMP/gh.tar.gz" + echo "$GH_ARCHIVE_SHA256 $RUNNER_TEMP/gh.tar.gz" | sha256sum --check --strict + tar -xzf "$RUNNER_TEMP/gh.tar.gz" -C "$RUNNER_TEMP" + echo "$RUNNER_TEMP/gh_2.100.0_linux_amd64/bin" >> "$GITHUB_PATH" + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ needs.verify-delivery.outputs.artifact }} + path: artifacts/release + - name: Verify existing assets or finalize the complete draft + env: + GH_TOKEN: ${{ github.token }} + run: node scripts/release.ts github + - name: Preserve completion or recovery status + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: github-status-${{ github.run_id }}-${{ github.run_attempt }} + path: artifacts/release/delivery-status.json + if-no-files-found: warn + retention-days: 30 diff --git a/AGENTS.md b/AGENTS.md index ea6a909..4f2160b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -78,7 +78,8 @@ inconclusive. Record exact revisions, artifact hashes and environment details. An independent reviewer checks the final implementation, tests, mathematical claims and owner principles before the draft PR is declared ready for review. -The reference branch remains unchanged. Do not merge main, publish packages or +See the [independent migration review](docs/reviews/2026-09-07-principles-review.md) +for the recorded checks and their limits. The reference branch remains unchanged. Do not merge main, publish packages or modify the upstream project as part of this migration. ## Repository hygiene diff --git a/README.md b/README.md index 43a085c..d789eca 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,37 @@ Production core and CLI coverage must be at least **80% each for statements, branches, functions and lines**. Type checking uses strict TypeScript 7; lint warnings are errors. +A [complete measured run](docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md) +compares all 45 locks on clean source +[`1651cd4`](https://github.com/Marcus-Rise/gothic-lock-solver/commit/1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9), +Node.js 24.19.0, Linux and AMD EPYC 9V74: + +| Implementation | Actions A | Distinct plates U | Unit shifts C | Sum of per-lock medians | +| --- | ---: | ---: | ---: | ---: | +| TypeScript library | 483 | 245 | 1,723 | 47.363 ms | +| Preserved matrix reference | 483 | 245 | 1,723 | 48.507 ms | +| Original BFS | 483 | 245 | 1,727 | 2,653.701 ms | +| Pinned Unlock My Loot | 491 | 245 | 1,695 | 1,517.403 ms | + +Against Unlock My Loot, five locks require fewer actions and forty tie, with +no action-count losses. It optimizes individual shifts first, so its smaller C +is a different trade-off. The TypeScript migration preserves A/U/C for every +lock compared with our matrix reference. + +Timing against the matrix reference is **inconclusive**: six per-lock comparisons +passed and thirty-nine remain uncertain. One bounded repeat detected a roughly +7 μs slowdown on `alberto-mine-hut-chest`; the conflicting attempts are preserved, +not dismissed. A smaller aggregate does not prove every lock became faster. +Whole-catalog process-memory comparison passed: median Linux VmHWM was +90.04 MiB versus 91.45 MiB. This includes Node and the harness, not just solver +allocations. The time column sums per-lock medians; it is not one measured +whole-catalog latency. + +The [machine-readable evidence](docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark-evidence.json) +links the complete raw samples, paths, environment and hashes. Seven timing +repetitions after two warmups, unchanged controls and bounded repeats make the +limits of these observations explicit. + [Benchmark documentation](benchmarks/README.md) describes the full reports, immutable fixtures, paired timing, peak RSS and baseline comparisons. Actions, distinct selected plates and individual shifts are reported separately. diff --git a/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark-evidence.json b/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark-evidence.json new file mode 100644 index 0000000..f693304 --- /dev/null +++ b/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark-evidence.json @@ -0,0 +1,5953 @@ +{ + "schemaVersion": 1, + "createdAt": "2026-09-07T09:12:21.367Z", + "quality": "passed", + "performance": "inconclusive", + "qualitySnapshot": { + "schemaVersion": 1, + "fixtureSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", + "source": { + "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", + "moduleSha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b", + "format": "tuple", + "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/dist/gothic-lock-solver.mjs" + }, + "reason": "Measured candidate paths and quality in this immutable benchmark evidence.", + "locks": [ + { + "id": "ruined-tower-door", + "optimumA": 17, + "A": 17, + "U": 6, + "C": 57, + "plateSwitches": 16, + "commands": [ + [ + 0, + -4 + ], + [ + 2, + 5 + ], + [ + 3, + -6 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 5, + -2 + ], + [ + 0, + -4 + ], + [ + 1, + 3 + ], + [ + 3, + -5 + ], + [ + 4, + -3 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 1, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "scatty-chest", + "optimumA": 9, + "A": 9, + "U": 5, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + 5 + ], + [ + 4, + 5 + ], + [ + 1, + -1 + ], + [ + 3, + 5 + ], + [ + 4, + 2 + ], + [ + 0, + -2 + ], + [ + 2, + 2 + ], + [ + 4, + 6 + ], + [ + 1, + -3 + ] + ] + }, + { + "id": "cor-galom-chest", + "optimumA": 5, + "A": 5, + "U": 4, + "C": 15, + "plateSwitches": 4, + "commands": [ + [ + 3, + 4 + ], + [ + 1, + -4 + ], + [ + 0, + 4 + ], + [ + 1, + -1 + ], + [ + 2, + -2 + ] + ] + }, + { + "id": "cor-galom-bedroom-chest", + "optimumA": 14, + "A": 14, + "U": 5, + "C": 60, + "plateSwitches": 13, + "commands": [ + [ + 1, + -5 + ], + [ + 2, + -4 + ], + [ + 1, + -4 + ], + [ + 4, + -4 + ], + [ + 1, + -4 + ], + [ + 3, + 4 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 2, + -2 + ], + [ + 1, + -6 + ], + [ + 4, + -6 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "swamp-camp-forge-chest", + "optimumA": 11, + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 2, + 6 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + -5 + ], + [ + 2, + 5 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 1, + -1 + ], + [ + 2, + 5 + ], + [ + 3, + -3 + ], + [ + 4, + -2 + ] + ] + }, + { + "id": "rice-lord-storeroom", + "optimumA": 9, + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 1 + ], + [ + 4, + 3 + ], + [ + 2, + 4 + ], + [ + 1, + 2 + ], + [ + 0, + 3 + ], + [ + 3, + 5 + ], + [ + 4, + 3 + ], + [ + 1, + 6 + ], + [ + 0, + 3 + ] + ] + }, + { + "id": "gomez-chambers-door", + "optimumA": 9, + "A": 9, + "U": 6, + "C": 28, + "plateSwitches": 8, + "commands": [ + [ + 3, + -3 + ], + [ + 2, + 2 + ], + [ + 1, + -2 + ], + [ + 0, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -4 + ], + [ + 5, + -4 + ], + [ + 3, + -5 + ], + [ + 1, + -3 + ] + ] + }, + { + "id": "gomez-throne-hall-chest", + "optimumA": 5, + "A": 5, + "U": 4, + "C": 16, + "plateSwitches": 4, + "commands": [ + [ + 3, + 6 + ], + [ + 0, + 6 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 3, + 1 + ] + ] + }, + { + "id": "old-camp-dungeon-door", + "optimumA": 17, + "A": 17, + "U": 6, + "C": 72, + "plateSwitches": 16, + "commands": [ + [ + 4, + -2 + ], + [ + 1, + -1 + ], + [ + 3, + 2 + ], + [ + 2, + -4 + ], + [ + 5, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + -5 + ], + [ + 5, + -6 + ], + [ + 2, + -6 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 1, + -6 + ], + [ + 0, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ], + [ + 5, + -6 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "arlin-dungeon-key", + "optimumA": 7, + "A": 7, + "U": 5, + "C": 25, + "plateSwitches": 6, + "commands": [ + [ + 2, + -2 + ], + [ + 5, + -2 + ], + [ + 6, + -6 + ], + [ + 0, + -3 + ], + [ + 2, + -6 + ], + [ + 1, + 3 + ], + [ + 5, + -3 + ] + ] + }, + { + "id": "gomez-wine-cellar", + "optimumA": 9, + "A": 9, + "U": 6, + "C": 41, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 2, + 5 + ], + [ + 5, + 4 + ], + [ + 3, + 4 + ], + [ + 2, + -6 + ], + [ + 4, + -5 + ], + [ + 3, + 6 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ] + ] + }, + { + "id": "gomez-bedside-chest", + "optimumA": 23, + "A": 23, + "U": 5, + "C": 68, + "plateSwitches": 22, + "commands": [ + [ + 2, + -1 + ], + [ + 1, + -1 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -4 + ], + [ + 0, + -4 + ], + [ + 1, + -4 + ], + [ + 4, + 2 + ], + [ + 2, + -2 + ], + [ + 3, + -5 + ], + [ + 0, + -4 + ], + [ + 1, + -6 + ], + [ + 4, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + 3 + ], + [ + 1, + -6 + ], + [ + 0, + -3 + ] + ] + }, + { + "id": "gomez-wall-chest", + "optimumA": 20, + "A": 20, + "U": 6, + "C": 75, + "plateSwitches": 19, + "commands": [ + [ + 0, + -2 + ], + [ + 4, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ], + [ + 5, + -6 + ], + [ + 3, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 3, + -3 + ], + [ + 5, + -5 + ], + [ + 0, + -2 + ], + [ + 5, + -2 + ], + [ + 2, + 4 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ], + [ + 1, + -3 + ] + ] + }, + { + "id": "voran-bedside-chest", + "optimumA": 9, + "A": 9, + "U": 7, + "C": 38, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 4, + -1 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 3, + 6 + ], + [ + 5, + 6 + ], + [ + 3, + 6 + ], + [ + 6, + -5 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "arto-room-door", + "optimumA": 7, + "A": 7, + "U": 5, + "C": 31, + "plateSwitches": 6, + "commands": [ + [ + 1, + -6 + ], + [ + 0, + -6 + ], + [ + 1, + -1 + ], + [ + 3, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + 5 + ], + [ + 2, + -3 + ] + ] + }, + { + "id": "arto-far-bed-chest", + "optimumA": 11, + "A": 11, + "U": 5, + "C": 44, + "plateSwitches": 10, + "commands": [ + [ + 1, + 4 + ], + [ + 2, + -2 + ], + [ + 5, + -2 + ], + [ + 3, + 3 + ], + [ + 4, + 6 + ], + [ + 1, + 6 + ], + [ + 5, + -6 + ], + [ + 4, + 6 + ], + [ + 1, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "arto-near-bed-chest", + "optimumA": 13, + "A": 13, + "U": 6, + "C": 52, + "plateSwitches": 12, + "commands": [ + [ + 4, + -1 + ], + [ + 0, + 6 + ], + [ + 5, + -3 + ], + [ + 3, + 4 + ], + [ + 2, + 6 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 3, + 4 + ], + [ + 0, + 6 + ], + [ + 2, + 4 + ], + [ + 5, + -6 + ], + [ + 1, + -3 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "gomez-bedrooms-door", + "optimumA": 14, + "A": 14, + "U": 6, + "C": 49, + "plateSwitches": 13, + "commands": [ + [ + 3, + -6 + ], + [ + 5, + -2 + ], + [ + 2, + 2 + ], + [ + 0, + 3 + ], + [ + 1, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + 3 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 5, + 3 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ] + ] + }, + { + "id": "gomez-bedrooms-far-chest", + "optimumA": 14, + "A": 14, + "U": 5, + "C": 43, + "plateSwitches": 13, + "commands": [ + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 3, + 2 + ], + [ + 4, + -3 + ], + [ + 2, + -4 + ], + [ + 0, + -5 + ], + [ + 1, + -4 + ], + [ + 0, + -2 + ], + [ + 3, + 2 + ], + [ + 2, + -3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "gomez-bedrooms-near-chest", + "optimumA": 12, + "A": 12, + "U": 6, + "C": 56, + "plateSwitches": 11, + "commands": [ + [ + 0, + 4 + ], + [ + 2, + 1 + ], + [ + 5, + -6 + ], + [ + 4, + -6 + ], + [ + 0, + 6 + ], + [ + 5, + -6 + ], + [ + 1, + -6 + ], + [ + 0, + 6 + ], + [ + 3, + -3 + ], + [ + 0, + 3 + ], + [ + 5, + -6 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "raven-tower-left-chest", + "optimumA": 11, + "A": 11, + "U": 6, + "C": 35, + "plateSwitches": 10, + "commands": [ + [ + 4, + 2 + ], + [ + 5, + -3 + ], + [ + 3, + 2 + ], + [ + 0, + 5 + ], + [ + 2, + -4 + ], + [ + 4, + 5 + ], + [ + 5, + -1 + ], + [ + 1, + -3 + ], + [ + 3, + 5 + ], + [ + 0, + 3 + ], + [ + 1, + -2 + ] + ] + }, + { + "id": "raven-tower-right-chest", + "optimumA": 8, + "A": 8, + "U": 6, + "C": 23, + "plateSwitches": 7, + "commands": [ + [ + 1, + 5 + ], + [ + 4, + 1 + ], + [ + 5, + -6 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -2 + ], + [ + 3, + 2 + ], + [ + 5, + -1 + ] + ] + }, + { + "id": "raven-tower-floor2-stairs-chest", + "optimumA": 16, + "A": 16, + "U": 6, + "C": 48, + "plateSwitches": 15, + "commands": [ + [ + 5, + 3 + ], + [ + 0, + -1 + ], + [ + 4, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + 4 + ], + [ + 3, + -2 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 1, + 6 + ], + [ + 2, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ] + ] + }, + { + "id": "raven-tower-floor2-chest-2", + "optimumA": 8, + "A": 8, + "U": 5, + "C": 29, + "plateSwitches": 7, + "commands": [ + [ + 3, + 1 + ], + [ + 2, + -5 + ], + [ + 4, + 5 + ], + [ + 3, + -3 + ], + [ + 0, + -1 + ], + [ + 1, + 5 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ] + ] + }, + { + "id": "raven-tower-floor3-entrance-chest", + "optimumA": 14, + "A": 14, + "U": 5, + "C": 44, + "plateSwitches": 13, + "commands": [ + [ + 3, + -4 + ], + [ + 1, + 1 + ], + [ + 4, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + 2 + ], + [ + 4, + -4 + ], + [ + 2, + -4 + ], + [ + 1, + 3 + ], + [ + 3, + -6 + ], + [ + 4, + -3 + ], + [ + 0, + -3 + ], + [ + 1, + 3 + ], + [ + 4, + -5 + ], + [ + 0, + -2 + ] + ] + }, + { + "id": "raven-tower-floor3-empty-chest", + "optimumA": 9, + "A": 9, + "U": 5, + "C": 37, + "plateSwitches": 8, + "commands": [ + [ + 0, + -2 + ], + [ + 4, + 6 + ], + [ + 2, + 3 + ], + [ + 1, + -3 + ], + [ + 4, + 6 + ], + [ + 3, + -4 + ], + [ + 0, + -6 + ], + [ + 4, + 4 + ], + [ + 2, + 3 + ] + ] + }, + { + "id": "raven-tower-floor4-elixirs-chest", + "optimumA": 8, + "A": 8, + "U": 5, + "C": 36, + "plateSwitches": 7, + "commands": [ + [ + 1, + -5 + ], + [ + 2, + -5 + ], + [ + 1, + -5 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ], + [ + 2, + -6 + ], + [ + 1, + -3 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "raven-tower-floor4-food-chest", + "optimumA": 11, + "A": 11, + "U": 6, + "C": 43, + "plateSwitches": 10, + "commands": [ + [ + 2, + 4 + ], + [ + 0, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 6 + ], + [ + 0, + 5 + ], + [ + 3, + -1 + ], + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + 2 + ], + [ + 5, + -3 + ] + ] + }, + { + "id": "raven-tower-top-chest", + "optimumA": 20, + "A": 20, + "U": 6, + "C": 48, + "plateSwitches": 19, + "commands": [ + [ + 1, + -3 + ], + [ + 3, + 1 + ], + [ + 0, + -1 + ], + [ + 4, + -2 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 3, + 2 + ], + [ + 5, + -4 + ], + [ + 2, + -1 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + -6 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "fire-lizard-cave-chest", + "optimumA": 10, + "A": 10, + "U": 6, + "C": 41, + "plateSwitches": 9, + "commands": [ + [ + 0, + -3 + ], + [ + 2, + -5 + ], + [ + 4, + 4 + ], + [ + 1, + 3 + ], + [ + 5, + -5 + ], + [ + 0, + -6 + ], + [ + 3, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + 6 + ], + [ + 0, + -3 + ] + ] + }, + { + "id": "silas-tavern-basement-chest", + "optimumA": 9, + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 4 + ], + [ + 3, + -3 + ], + [ + 1, + -4 + ], + [ + 3, + -3 + ], + [ + 4, + 6 + ], + [ + 2, + 3 + ], + [ + 3, + -3 + ] + ] + }, + { + "id": "silas-tavern-storeroom-chest", + "optimumA": 9, + "A": 9, + "U": 6, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + -3 + ], + [ + 3, + 4 + ], + [ + 5, + -6 + ], + [ + 0, + 2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -6 + ], + [ + 3, + 3 + ], + [ + 5, + -3 + ] + ] + }, + { + "id": "silas-tavern-storeroom-door", + "optimumA": 10, + "A": 10, + "U": 5, + "C": 31, + "plateSwitches": 9, + "commands": [ + [ + 3, + 2 + ], + [ + 5, + -3 + ], + [ + 1, + -3 + ], + [ + 5, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + 3 + ], + [ + 5, + -3 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 2, + -3 + ] + ] + }, + { + "id": "jan-pond-house-chest", + "optimumA": 8, + "A": 8, + "U": 6, + "C": 33, + "plateSwitches": 7, + "commands": [ + [ + 0, + -4 + ], + [ + 2, + -6 + ], + [ + 3, + 4 + ], + [ + 4, + 3 + ], + [ + 5, + -3 + ], + [ + 3, + 5 + ], + [ + 5, + -5 + ], + [ + 1, + 3 + ] + ] + }, + { + "id": "graham-chest", + "optimumA": 10, + "A": 10, + "U": 6, + "C": 40, + "plateSwitches": 9, + "commands": [ + [ + 1, + 5 + ], + [ + 4, + 6 + ], + [ + 0, + -5 + ], + [ + 1, + 6 + ], + [ + 4, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 2 + ], + [ + 3, + -3 + ], + [ + 4, + 2 + ], + [ + 5, + -1 + ] + ] + }, + { + "id": "gravo-chest", + "optimumA": 8, + "A": 8, + "U": 5, + "C": 27, + "plateSwitches": 7, + "commands": [ + [ + 0, + -5 + ], + [ + 4, + -1 + ], + [ + 1, + 3 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + -6 + ], + [ + 1, + 3 + ] + ] + }, + { + "id": "diego-chest", + "optimumA": 11, + "A": 11, + "U": 6, + "C": 40, + "plateSwitches": 10, + "commands": [ + [ + 0, + 2 + ], + [ + 1, + 1 + ], + [ + 2, + -6 + ], + [ + 3, + -1 + ], + [ + 1, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + -6 + ], + [ + 5, + 3 + ], + [ + 1, + 6 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ] + ] + }, + { + "id": "grim-chest", + "optimumA": 8, + "A": 8, + "U": 6, + "C": 21, + "plateSwitches": 7, + "commands": [ + [ + 4, + -2 + ], + [ + 3, + 1 + ], + [ + 1, + 6 + ], + [ + 2, + -1 + ], + [ + 0, + -5 + ], + [ + 3, + 4 + ], + [ + 0, + -1 + ], + [ + 5, + -1 + ] + ] + }, + { + "id": "mercenary-near-grim-chest", + "optimumA": 8, + "A": 8, + "U": 6, + "C": 25, + "plateSwitches": 7, + "commands": [ + [ + 2, + 3 + ], + [ + 1, + 3 + ], + [ + 3, + -6 + ], + [ + 5, + 6 + ], + [ + 0, + -2 + ], + [ + 3, + -2 + ], + [ + 4, + 2 + ], + [ + 5, + 1 + ] + ] + }, + { + "id": "popular-lock", + "optimumA": 13, + "A": 13, + "U": 5, + "C": 46, + "plateSwitches": 12, + "commands": [ + [ + 4, + -4 + ], + [ + 2, + -1 + ], + [ + 3, + -2 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + 6 + ], + [ + 2, + -3 + ], + [ + 4, + -6 + ], + [ + 1, + -3 + ], + [ + 0, + 3 + ], + [ + 3, + -3 + ], + [ + 4, + -6 + ], + [ + 0, + 3 + ] + ] + }, + { + "id": "alberto-mine-hut-chest", + "optimumA": 9, + "A": 9, + "U": 5, + "C": 36, + "plateSwitches": 8, + "commands": [ + [ + 3, + -4 + ], + [ + 1, + -2 + ], + [ + 4, + 6 + ], + [ + 0, + -3 + ], + [ + 3, + -6 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + 3 + ] + ] + }, + { + "id": "alberto-mine-upstairs-left-chest", + "optimumA": 4, + "A": 4, + "U": 3, + "C": 20, + "plateSwitches": 3, + "commands": [ + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 1, + 6 + ], + [ + 3, + 3 + ] + ] + }, + { + "id": "alberto-mine-upstairs-right-chest", + "optimumA": 5, + "A": 5, + "U": 5, + "C": 13, + "plateSwitches": 4, + "commands": [ + [ + 0, + 3 + ], + [ + 1, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 4 + ], + [ + 3, + 2 + ] + ] + }, + { + "id": "cavalorn-storeroom-door", + "optimumA": 11, + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 3, + -2 + ], + [ + 5, + 4 + ], + [ + 0, + -1 + ], + [ + 1, + -6 + ], + [ + 0, + -4 + ], + [ + 3, + -2 + ], + [ + 1, + -6 + ], + [ + 4, + -4 + ], + [ + 5, + 6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ] + ] + }, + { + "id": "old-to-new-camp-bandits-chest", + "optimumA": 10, + "A": 10, + "U": 5, + "C": 33, + "plateSwitches": 9, + "commands": [ + [ + 4, + 6 + ], + [ + 5, + -2 + ], + [ + 2, + -1 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ] + ] + } + ] + }, + "metadata": { + "environment": { + "node": "v24.19.0", + "v8": "13.6.233.17-node.51", + "platform": "linux", + "arch": "x64", + "cpu": "AMD EPYC 9V74 80-Core Processor", + "logicalCpus": 9, + "osRelease": "6.18.35", + "totalMemoryBytes": 23109910528, + "execArgv": [] + }, + "source": { + "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", + "dirty": false, + "files": { + "errors.ts": "830e0995bb1d5b98e8ce0495a22e5ae169cab10b2bdff2612e58323c397306f5", + "index.ts": "a1a2eedf5010cd66fc5d451035923e6ab417b36ba84cb2da85789cea17a1569a", + "indexed-heap.ts": "f776755f7405968b567922473c1e6ea5a63dae7643c4bd62b993159e900b7fbc", + "indexed.ts": "279b11d625a033f45723dd94df13a9be9fdfe267cff2535d5470b2011f11e125", + "lock-model.ts": "b4674921412e2acfbf0eaeff2031b3fc3907dffe886c42e588cd5ba6348ec966", + "matrix-analysis.ts": "a105dee650d645a730e3cc7e62a8e9faae31b29ad98c28aafce5ef15df236fc6", + "matrix-search.ts": "380ae745289a7ffd1997a6b9aa7dc3dd06a56e17453732d41042891c42435d3a", + "search-bfs.ts": "011c675973ccc8d753c0b9b3608882b16ca93ef4b460635c3474d8d5fc705feb", + "search-limits.ts": "91afc0940d49af46d9bb57d91af45df992f0684593d2f2d8bc3c13fad3f4c681", + "types.ts": "a2e72cf02e31694c52c1621cf5359863585b8383aa83a7d4e977c8547e8c879b" + }, + "harness": { + "catalog.ts": "2b75c9e9b5fe6997006307bf74cd90d8645d6d61fc527681f209c164680b880a", + "cli.ts": "b717c3d69c13d4a5b9a77589b1465f96338fe9d8be6501249f4470db035efe33", + "comparison.ts": "680ac76d266b51ff1f951fd3e232248bc5df0ab9eb70123285d31d176fb3a709", + "harness.ts": "14d23d697b2c4dd8adc13beac84b1cd3c0236aa34ae42ea96935fb30ac75e9f8", + "memory.ts": "429b45f541a2ac97e46fb6598ac30966cab4e2dca7e355c56f3ad3e037f43d93", + "rss.ts": "acc321bc34deac44819e5ff6d9af89e91c7394d9d897f7d69ce0cfedfc2e992c", + "solver.bench.ts": "985fbba3b387109261abeb1fdcbecd91b0ccfaa4dde5e0f162ad1758d529647f", + "validation.ts": "c44fc2a3de4da26ab207186a97a6b80ab20a910d27ae335da6cec536e402d77d" + }, + "scripts": { + "benchmark-compare.ts": "d4243c84a0a007b611e04e9c07031c3a7e48fb1a610366349084f0a7ab61cd71", + "benchmark-memory-worker.ts": "807707ca177f43c5ef08b88aa1068ad5f08f391b74f7f8d358159a2e5bf8a49b", + "benchmark-memory.ts": "c25a0407122f1c16261d8f8f9fd6f60cba463a7e288579ba67fde398db8d854f", + "benchmark-snapshot.ts": "50b31ffeac808f5b21f0fb864d2efd8a7c758491cf4e2d611bbe900bf3f9ec32", + "benchmark.ts": "fdb3f22dfdd495a6dde6a27fc3b0db68c7ceb75b94acaff131d30a25b6a06f24" + } + }, + "candidate": { + "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", + "moduleSha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b", + "format": "tuple", + "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/dist/gothic-lock-solver.mjs" + }, + "baseline": { + "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", + "moduleSha256": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "format": "legacy", + "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/reference-matrix/src/index.mjs", + "moduleTree": { + "cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", + "index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", + "matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", + "matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", + "result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", + "search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", + "search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", + "solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", + "state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", + "transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" + }, + "snapshotSha256": "c21b6319523f625439403c0417d267b5a63c35abafebef758bfc6c21217a3b94", + "snapshot": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/snapshots/reference-matrix.json", + "kind": "reference-baseline" + }, + "fixtures": { + "count": 45, + "catalogSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", + "manifestSha256": "a634d5328fd8d41470397b622ae0bd32b8aac0355dfc31975e023a63a62e65c9" + }, + "originalBfs": { + "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", + "revision": "a87e739a22ccc4215d6d9b14fa06b0738171cb60", + "license": "Apache-2.0", + "files": { + "src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", + "src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", + "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", + "src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", + "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", + "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", + "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" + }, + "notes": "Unmodified source snapshots; imports remain relative within this directory. No upstream UnlockMyLoot solver code is included." + }, + "matrixReference": { + "schemaVersion": 1, + "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", + "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", + "license": "Apache-2.0", + "files": { + "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "src/cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", + "src/index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "src/lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", + "src/matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", + "src/matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", + "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", + "src/search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", + "src/search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", + "src/solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", + "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", + "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" + }, + "notes": "Byte-identical Apache matrix reference, copied from source at the pinned revision. Benchmark-only; excluded from runtime and npm package. No external AGPL solver source." + }, + "upstream": { + "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", + "solver": { + "path": "index.html", + "sha256": "691f744099a9169f30e9883fddc5fdbb4949d306f3011e601678419d7b93821b", + "blockSha256": "d5bc85f40fd7c5b61097403df399ecb7dfd2b58924dc7a670aa0d05306017380", + "startMarker": "/* solver-start", + "endMarker": "/* solver-end */" + }, + "measured": true, + "externalCheckout": "/workspace/scratch/5d94b8672fa4/unlockmyloot", + "license": "External AGPL source is hash-verified and executed only from the supplied pinned checkout; no upstream solver code is vendored." + }, + "artifacts": { + "gothic-lock-solver.mjs": { + "bytes": 20143, + "sha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b" + }, + "gothic-lock-solver.min.mjs": { + "bytes": 9098, + "sha256": "9a7c0d365a0e2630372200f4c8a7914024b04afd048ca77e6a6783c120676d1f" + }, + "gothic-lock-solver.js": { + "bytes": 20916, + "sha256": "14841c4bc1563f4fd8beae3e277798878a2862efa0840e50b1aef9d5e5dff101" + }, + "gothic-lock-solver.min.js": { + "bytes": 9197, + "sha256": "2a936f190ae38f458e2b209681beb099974f32d654eb33be7249be6ede847694" + }, + "gothic-lock-solver.cli.mjs": { + "bytes": 23199, + "sha256": "75129dff219af243b05ffcbdb63ac1a14a32fd0d0b5ae8ee4528bb4d24b75f81" + } + } + }, + "memory": { + "metric": "Linux /proc/self/status VmHWM × 1024 bytes (current process-image RSS high-water)", + "repetitions": 5, + "sources": [ + "https://docs.kernel.org/filesystems/proc.html", + "https://nodejs.org/api/process.html#processresourceusage" + ], + "accuracy": "Kernel RSS accounting is asynchronous and approximate; this is not an exact allocation count. Raw getrusage maxRSS and startup/current-image peaks are preserved to expose pre-exec launch floors.", + "scope": "Each per-lock observation is a fresh process. Repeated comparison is peak RSS of the selected 45-lock workload per fresh process.", + "includes": "Node runtime, TypeScript benchmark loader, module loading, fixture loading, normalization, independent replay and solver allocations. Startup and loaded current RSS are recorded separately; subtraction is not exact algorithm allocation.", + "browser": { + "available": false, + "reason": "No portable exact browser peak-memory API; Node RSS is not browser memory." + }, + "performance": { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.962246756993597, + "noiseRatio": 1.0949647700001646, + "lowerRatio": 0.8908822461111241, + "upperRatio": 1.0393279531458977 + }, + "attempts": [ + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.962246756993597, + "noiseRatio": 1.0949647700001646, + "lowerRatio": 0.8908822461111241, + "upperRatio": 1.0393279531458977, + "samples": { + "candidate": [ + { + "peakRssBytes": 94666752, + "startupRssBytes": 64344064, + "loadedRssBytes": 65785856, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65351680, + "loadedPeakRssBytes": 66846720, + "resourceUsageMaxRssBytes": 93732864, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94412800, + "startupRssBytes": 65175552, + "loadedRssBytes": 66617344, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66248704, + "loadedPeakRssBytes": 67477504, + "resourceUsageMaxRssBytes": 93741056, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94752768, + "startupRssBytes": 65814528, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66715648, + "loadedPeakRssBytes": 67567616, + "resourceUsageMaxRssBytes": 94523392, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89186304, + "startupRssBytes": 65310720, + "loadedRssBytes": 66883584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66576384, + "loadedPeakRssBytes": 67739648, + "resourceUsageMaxRssBytes": 89006080, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84447232, + "startupRssBytes": 65482752, + "loadedRssBytes": 67186688, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66744320, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "baseline": [ + { + "peakRssBytes": 95948800, + "startupRssBytes": 64970752, + "loadedRssBytes": 67461120, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66179072, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 95100928, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89391104, + "startupRssBytes": 64778240, + "loadedRssBytes": 67137536, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65925120, + "loadedPeakRssBytes": 68210688, + "resourceUsageMaxRssBytes": 88641536, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 100007936, + "startupRssBytes": 64532480, + "loadedRssBytes": 67678208, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65564672, + "loadedPeakRssBytes": 68284416, + "resourceUsageMaxRssBytes": 100007936, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95895552, + "startupRssBytes": 65687552, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67055616, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 95895552, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 93995008, + "startupRssBytes": 65671168, + "loadedRssBytes": 67375104, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66916352, + "loadedPeakRssBytes": 68136960, + "resourceUsageMaxRssBytes": 93188096, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "originalBfs": [ + { + "peakRssBytes": 121143296, + "startupRssBytes": 65421312, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66490368, + "loadedPeakRssBytes": 67485696, + "resourceUsageMaxRssBytes": 120414208, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120922112, + "startupRssBytes": 65372160, + "loadedRssBytes": 67207168, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 120160256, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120438784, + "startupRssBytes": 64557056, + "loadedRssBytes": 66392064, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65339392, + "loadedPeakRssBytes": 67448832, + "resourceUsageMaxRssBytes": 119697408, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120311808, + "startupRssBytes": 65306624, + "loadedRssBytes": 67141632, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66830336, + "loadedPeakRssBytes": 68071424, + "resourceUsageMaxRssBytes": 119595008, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 121356288, + "startupRssBytes": 65691648, + "loadedRssBytes": 67395584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 68116480, + "resourceUsageMaxRssBytes": 120606720, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "upstream": [ + { + "peakRssBytes": 270356480, + "startupRssBytes": 65941504, + "loadedRssBytes": 68431872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67108864, + "loadedPeakRssBytes": 69251072, + "resourceUsageMaxRssBytes": 269815808, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270110720, + "startupRssBytes": 65540096, + "loadedRssBytes": 68685824, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 69443584, + "resourceUsageMaxRssBytes": 269246464, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 268992512, + "startupRssBytes": 64909312, + "loadedRssBytes": 68710400, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65966080, + "loadedPeakRssBytes": 69349376, + "resourceUsageMaxRssBytes": 267882496, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 309043200, + "startupRssBytes": 65306624, + "loadedRssBytes": 68452352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66768896, + "loadedPeakRssBytes": 69234688, + "resourceUsageMaxRssBytes": 309043200, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270135296, + "startupRssBytes": 65089536, + "loadedRssBytes": 68497408, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65785856, + "loadedPeakRssBytes": 69300224, + "resourceUsageMaxRssBytes": 269443072, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "baselineSelfA": [ + { + "peakRssBytes": 95563776, + "startupRssBytes": 65470464, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66633728, + "loadedPeakRssBytes": 68255744, + "resourceUsageMaxRssBytes": 95563776, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95862784, + "startupRssBytes": 65462272, + "loadedRssBytes": 67297280, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66928640, + "loadedPeakRssBytes": 68186112, + "resourceUsageMaxRssBytes": 95862784, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94965760, + "startupRssBytes": 65818624, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67014656, + "loadedPeakRssBytes": 68362240, + "resourceUsageMaxRssBytes": 94965760, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95129600, + "startupRssBytes": 65208320, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66326528, + "loadedPeakRssBytes": 68149248, + "resourceUsageMaxRssBytes": 94162944, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94371840, + "startupRssBytes": 64974848, + "loadedRssBytes": 66940928, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66097152, + "loadedPeakRssBytes": 67719168, + "resourceUsageMaxRssBytes": 94371840, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "baselineSelfB": [ + { + "peakRssBytes": 96808960, + "startupRssBytes": 64933888, + "loadedRssBytes": 67424256, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66068480, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 96731136, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94896128, + "startupRssBytes": 65703936, + "loadedRssBytes": 67407872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 94896128, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84250624, + "startupRssBytes": 65826816, + "loadedRssBytes": 67530752, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66949120, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 97107968, + "startupRssBytes": 65032192, + "loadedRssBytes": 66998272, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66183168, + "loadedPeakRssBytes": 67727360, + "resourceUsageMaxRssBytes": 97107968, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94167040, + "startupRssBytes": 64937984, + "loadedRssBytes": 67428352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66555904, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 93442048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ] + } + } + ], + "wholeCatalog": { + "candidate": { + "samples": [ + { + "peakRssBytes": 94666752, + "startupRssBytes": 64344064, + "loadedRssBytes": 65785856, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65351680, + "loadedPeakRssBytes": 66846720, + "resourceUsageMaxRssBytes": 93732864, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94412800, + "startupRssBytes": 65175552, + "loadedRssBytes": 66617344, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66248704, + "loadedPeakRssBytes": 67477504, + "resourceUsageMaxRssBytes": 93741056, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94752768, + "startupRssBytes": 65814528, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66715648, + "loadedPeakRssBytes": 67567616, + "resourceUsageMaxRssBytes": 94523392, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89186304, + "startupRssBytes": 65310720, + "loadedRssBytes": 66883584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66576384, + "loadedPeakRssBytes": 67739648, + "resourceUsageMaxRssBytes": 89006080, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84447232, + "startupRssBytes": 65482752, + "loadedRssBytes": 67186688, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66744320, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 84447232, + "median": 94412800, + "p95": 94752768, + "max": 94752768 + } + }, + "baseline": { + "samples": [ + { + "peakRssBytes": 95948800, + "startupRssBytes": 64970752, + "loadedRssBytes": 67461120, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66179072, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 95100928, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89391104, + "startupRssBytes": 64778240, + "loadedRssBytes": 67137536, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65925120, + "loadedPeakRssBytes": 68210688, + "resourceUsageMaxRssBytes": 88641536, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 100007936, + "startupRssBytes": 64532480, + "loadedRssBytes": 67678208, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65564672, + "loadedPeakRssBytes": 68284416, + "resourceUsageMaxRssBytes": 100007936, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95895552, + "startupRssBytes": 65687552, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67055616, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 95895552, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 93995008, + "startupRssBytes": 65671168, + "loadedRssBytes": 67375104, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66916352, + "loadedPeakRssBytes": 68136960, + "resourceUsageMaxRssBytes": 93188096, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 89391104, + "median": 95895552, + "p95": 100007936, + "max": 100007936 + } + }, + "originalBfs": { + "samples": [ + { + "peakRssBytes": 121143296, + "startupRssBytes": 65421312, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66490368, + "loadedPeakRssBytes": 67485696, + "resourceUsageMaxRssBytes": 120414208, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120922112, + "startupRssBytes": 65372160, + "loadedRssBytes": 67207168, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 120160256, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120438784, + "startupRssBytes": 64557056, + "loadedRssBytes": 66392064, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65339392, + "loadedPeakRssBytes": 67448832, + "resourceUsageMaxRssBytes": 119697408, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120311808, + "startupRssBytes": 65306624, + "loadedRssBytes": 67141632, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66830336, + "loadedPeakRssBytes": 68071424, + "resourceUsageMaxRssBytes": 119595008, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 121356288, + "startupRssBytes": 65691648, + "loadedRssBytes": 67395584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 68116480, + "resourceUsageMaxRssBytes": 120606720, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 120311808, + "median": 120922112, + "p95": 121356288, + "max": 121356288 + } + }, + "upstream": { + "samples": [ + { + "peakRssBytes": 270356480, + "startupRssBytes": 65941504, + "loadedRssBytes": 68431872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67108864, + "loadedPeakRssBytes": 69251072, + "resourceUsageMaxRssBytes": 269815808, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270110720, + "startupRssBytes": 65540096, + "loadedRssBytes": 68685824, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 69443584, + "resourceUsageMaxRssBytes": 269246464, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 268992512, + "startupRssBytes": 64909312, + "loadedRssBytes": 68710400, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65966080, + "loadedPeakRssBytes": 69349376, + "resourceUsageMaxRssBytes": 267882496, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 309043200, + "startupRssBytes": 65306624, + "loadedRssBytes": 68452352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66768896, + "loadedPeakRssBytes": 69234688, + "resourceUsageMaxRssBytes": 309043200, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270135296, + "startupRssBytes": 65089536, + "loadedRssBytes": 68497408, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65785856, + "loadedPeakRssBytes": 69300224, + "resourceUsageMaxRssBytes": 269443072, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 268992512, + "median": 270135296, + "p95": 309043200, + "max": 309043200 + } + }, + "baselineSelfA": { + "samples": [ + { + "peakRssBytes": 95563776, + "startupRssBytes": 65470464, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66633728, + "loadedPeakRssBytes": 68255744, + "resourceUsageMaxRssBytes": 95563776, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95862784, + "startupRssBytes": 65462272, + "loadedRssBytes": 67297280, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66928640, + "loadedPeakRssBytes": 68186112, + "resourceUsageMaxRssBytes": 95862784, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94965760, + "startupRssBytes": 65818624, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67014656, + "loadedPeakRssBytes": 68362240, + "resourceUsageMaxRssBytes": 94965760, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95129600, + "startupRssBytes": 65208320, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66326528, + "loadedPeakRssBytes": 68149248, + "resourceUsageMaxRssBytes": 94162944, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94371840, + "startupRssBytes": 64974848, + "loadedRssBytes": 66940928, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66097152, + "loadedPeakRssBytes": 67719168, + "resourceUsageMaxRssBytes": 94371840, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 94371840, + "median": 95129600, + "p95": 95862784, + "max": 95862784 + } + }, + "baselineSelfB": { + "samples": [ + { + "peakRssBytes": 96808960, + "startupRssBytes": 64933888, + "loadedRssBytes": 67424256, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66068480, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 96731136, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94896128, + "startupRssBytes": 65703936, + "loadedRssBytes": 67407872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 94896128, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84250624, + "startupRssBytes": 65826816, + "loadedRssBytes": 67530752, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66949120, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 97107968, + "startupRssBytes": 65032192, + "loadedRssBytes": 66998272, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66183168, + "loadedPeakRssBytes": 67727360, + "resourceUsageMaxRssBytes": 97107968, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94167040, + "startupRssBytes": 64937984, + "loadedRssBytes": 67428352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66555904, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 93442048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 84250624, + "median": 94896128, + "p95": 97107968, + "max": 97107968 + } + } + }, + "perLock": [ + { + "id": "ruined-tower-door", + "solvers": { + "candidate": { + "peakRssBytes": 77537280, + "startupRssBytes": 65085440, + "loadedRssBytes": 66658304, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66162688, + "loadedPeakRssBytes": 67620864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 74416128, + "startupRssBytes": 64368640, + "loadedRssBytes": 67383296, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65441792, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78462976, + "startupRssBytes": 65814528, + "loadedRssBytes": 67125248, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66789376, + "loadedPeakRssBytes": 67813376, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 106233856, + "startupRssBytes": 64303104, + "loadedRssBytes": 68497408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65495040, + "loadedPeakRssBytes": 69402624, + "resourceUsageMaxRssBytes": 105652224, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "scatty-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71573504, + "startupRssBytes": 65675264, + "loadedRssBytes": 67117056, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66748416, + "loadedPeakRssBytes": 67698688, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71282688, + "startupRssBytes": 65290240, + "loadedRssBytes": 67387392, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66768896, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78876672, + "startupRssBytes": 66211840, + "loadedRssBytes": 67260416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67403776, + "loadedPeakRssBytes": 68096000, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75640832, + "startupRssBytes": 65265664, + "loadedRssBytes": 68411392, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66207744, + "loadedPeakRssBytes": 69148672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "cor-galom-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72212480, + "startupRssBytes": 65150976, + "loadedRssBytes": 66592768, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66125824, + "loadedPeakRssBytes": 67354624, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71266304, + "startupRssBytes": 65617920, + "loadedRssBytes": 67452928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66957312, + "loadedPeakRssBytes": 68227072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 76562432, + "startupRssBytes": 65687552, + "loadedRssBytes": 67391488, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66940928, + "loadedPeakRssBytes": 68087808, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 73269248, + "startupRssBytes": 65531904, + "loadedRssBytes": 68415488, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67010560, + "loadedPeakRssBytes": 69345280, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "cor-galom-bedroom-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72265728, + "startupRssBytes": 65675264, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67072000, + "loadedPeakRssBytes": 68050944, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71311360, + "startupRssBytes": 65495040, + "loadedRssBytes": 67461120, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 68280320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78819328, + "startupRssBytes": 65101824, + "loadedRssBytes": 67198976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66678784, + "loadedPeakRssBytes": 68079616, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78999552, + "startupRssBytes": 65703936, + "loadedRssBytes": 68456448, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66990080, + "loadedPeakRssBytes": 69292032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "swamp-camp-forge-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72208384, + "startupRssBytes": 65228800, + "loadedRssBytes": 66670592, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66412544, + "loadedPeakRssBytes": 67661824, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71602176, + "startupRssBytes": 64950272, + "loadedRssBytes": 67571712, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65806336, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78815232, + "startupRssBytes": 65097728, + "loadedRssBytes": 67325952, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66359296, + "loadedPeakRssBytes": 68022272, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 83496960, + "startupRssBytes": 65830912, + "loadedRssBytes": 68714496, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67059712, + "loadedPeakRssBytes": 69357568, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "rice-lord-storeroom", + "solvers": { + "candidate": { + "peakRssBytes": 71999488, + "startupRssBytes": 65429504, + "loadedRssBytes": 66740224, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66564096, + "loadedPeakRssBytes": 67616768, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71303168, + "startupRssBytes": 65781760, + "loadedRssBytes": 67747840, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67104768, + "loadedPeakRssBytes": 68378624, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78467072, + "startupRssBytes": 65544192, + "loadedRssBytes": 66985984, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66797568, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75665408, + "startupRssBytes": 65040384, + "loadedRssBytes": 68448256, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65794048, + "loadedPeakRssBytes": 69210112, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-chambers-door", + "solvers": { + "candidate": { + "peakRssBytes": 72138752, + "startupRssBytes": 65363968, + "loadedRssBytes": 66936832, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66924544, + "loadedPeakRssBytes": 67854336, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71692288, + "startupRssBytes": 65499136, + "loadedRssBytes": 67203072, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66719744, + "loadedPeakRssBytes": 67993600, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79171584, + "startupRssBytes": 65282048, + "loadedRssBytes": 67248128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66682880, + "loadedPeakRssBytes": 68141056, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 80289792, + "startupRssBytes": 65380352, + "loadedRssBytes": 68395008, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67026944, + "loadedPeakRssBytes": 69320704, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-throne-hall-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71208960, + "startupRssBytes": 65286144, + "loadedRssBytes": 66727936, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66678784, + "loadedPeakRssBytes": 67715072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71991296, + "startupRssBytes": 65724416, + "loadedRssBytes": 67297280, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67112960, + "loadedPeakRssBytes": 68227072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 72019968, + "startupRssBytes": 65179648, + "loadedRssBytes": 67801088, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66400256, + "loadedPeakRssBytes": 68497408, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 72011776, + "startupRssBytes": 64962560, + "loadedRssBytes": 68632576, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66564096, + "loadedPeakRssBytes": 69357568, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "old-camp-dungeon-door", + "solvers": { + "candidate": { + "peakRssBytes": 77049856, + "startupRssBytes": 65257472, + "loadedRssBytes": 67092480, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66510848, + "loadedPeakRssBytes": 67887104, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 75661312, + "startupRssBytes": 65220608, + "loadedRssBytes": 67448832, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66314240, + "loadedPeakRssBytes": 68214784, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79208448, + "startupRssBytes": 63983616, + "loadedRssBytes": 66473984, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65093632, + "loadedPeakRssBytes": 67276800, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 85356544, + "startupRssBytes": 65171456, + "loadedRssBytes": 68448256, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66473984, + "loadedPeakRssBytes": 69226496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arlin-dungeon-key", + "solvers": { + "candidate": { + "peakRssBytes": 81268736, + "startupRssBytes": 65007616, + "loadedRssBytes": 66449408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66531328, + "loadedPeakRssBytes": 67469312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 79904768, + "startupRssBytes": 65724416, + "loadedRssBytes": 67428352, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66981888, + "loadedPeakRssBytes": 68276224, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 83525632, + "startupRssBytes": 64585728, + "loadedRssBytes": 67338240, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65556480, + "loadedPeakRssBytes": 68038656, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 76693504, + "startupRssBytes": 65495040, + "loadedRssBytes": 68509696, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66912256, + "loadedPeakRssBytes": 69320704, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-wine-cellar", + "solvers": { + "candidate": { + "peakRssBytes": 71606272, + "startupRssBytes": 65417216, + "loadedRssBytes": 66859008, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66609152, + "loadedPeakRssBytes": 67608576, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71225344, + "startupRssBytes": 65245184, + "loadedRssBytes": 67473408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66383872, + "loadedPeakRssBytes": 68194304, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79204352, + "startupRssBytes": 65544192, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66928640, + "loadedPeakRssBytes": 68059136, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75743232, + "startupRssBytes": 65306624, + "loadedRssBytes": 68583424, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 69271552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedside-chest", + "solvers": { + "candidate": { + "peakRssBytes": 76054528, + "startupRssBytes": 65183744, + "loadedRssBytes": 66887680, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66506752, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73519104, + "startupRssBytes": 64593920, + "loadedRssBytes": 67215360, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65310720, + "loadedPeakRssBytes": 67936256, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78987264, + "startupRssBytes": 64577536, + "loadedRssBytes": 67198976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65683456, + "loadedPeakRssBytes": 67956736, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78557184, + "startupRssBytes": 65847296, + "loadedRssBytes": 68599808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67043328, + "loadedPeakRssBytes": 69226496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-wall-chest", + "solvers": { + "candidate": { + "peakRssBytes": 79142912, + "startupRssBytes": 65122304, + "loadedRssBytes": 66957312, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66228224, + "loadedPeakRssBytes": 67846144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 76414976, + "startupRssBytes": 64720896, + "loadedRssBytes": 67080192, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65634304, + "loadedPeakRssBytes": 67944448, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79220736, + "startupRssBytes": 65282048, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66985984, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 111869952, + "startupRssBytes": 64249856, + "loadedRssBytes": 68444160, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65085440, + "loadedPeakRssBytes": 69144576, + "resourceUsageMaxRssBytes": 111263744, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "voran-bedside-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71966720, + "startupRssBytes": 65683456, + "loadedRssBytes": 66994176, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67055616, + "loadedPeakRssBytes": 67956736, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71983104, + "startupRssBytes": 65404928, + "loadedRssBytes": 67502080, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66428928, + "loadedPeakRssBytes": 68198400, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 83914752, + "startupRssBytes": 65208320, + "loadedRssBytes": 67436544, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66514944, + "loadedPeakRssBytes": 68112384, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 123584512, + "startupRssBytes": 65044480, + "loadedRssBytes": 68190208, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66375680, + "loadedPeakRssBytes": 69156864, + "resourceUsageMaxRssBytes": 122744832, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arto-room-door", + "solvers": { + "candidate": { + "peakRssBytes": 71913472, + "startupRssBytes": 65241088, + "loadedRssBytes": 66551808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66400256, + "loadedPeakRssBytes": 67444736, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70885376, + "startupRssBytes": 65437696, + "loadedRssBytes": 67272704, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66719744, + "loadedPeakRssBytes": 67952640, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78823424, + "startupRssBytes": 65675264, + "loadedRssBytes": 67248128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67026944, + "loadedPeakRssBytes": 68091904, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74354688, + "startupRssBytes": 65679360, + "loadedRssBytes": 68038656, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66564096, + "loadedPeakRssBytes": 68825088, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arto-far-bed-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72302592, + "startupRssBytes": 65015808, + "loadedRssBytes": 66326528, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66314240, + "loadedPeakRssBytes": 67268608, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71376896, + "startupRssBytes": 65216512, + "loadedRssBytes": 67575808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66478080, + "loadedPeakRssBytes": 68255744, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78929920, + "startupRssBytes": 65646592, + "loadedRssBytes": 67219456, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66936832, + "loadedPeakRssBytes": 68046848, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74788864, + "startupRssBytes": 65531904, + "loadedRssBytes": 68546560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66867200, + "loadedPeakRssBytes": 69308416, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arto-near-bed-chest", + "solvers": { + "candidate": { + "peakRssBytes": 73064448, + "startupRssBytes": 64270336, + "loadedRssBytes": 65974272, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65445888, + "loadedPeakRssBytes": 66961408, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71794688, + "startupRssBytes": 64516096, + "loadedRssBytes": 66088960, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65302528, + "loadedPeakRssBytes": 66691072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78839808, + "startupRssBytes": 65679360, + "loadedRssBytes": 67383296, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66977792, + "loadedPeakRssBytes": 68071424, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 110370816, + "startupRssBytes": 64880640, + "loadedRssBytes": 68419584, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66088960, + "loadedPeakRssBytes": 69353472, + "resourceUsageMaxRssBytes": 109670400, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedrooms-door", + "solvers": { + "candidate": { + "peakRssBytes": 79302656, + "startupRssBytes": 65355776, + "loadedRssBytes": 66928640, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66699264, + "loadedPeakRssBytes": 67833856, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 78962688, + "startupRssBytes": 65097728, + "loadedRssBytes": 67325952, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66465792, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79081472, + "startupRssBytes": 64839680, + "loadedRssBytes": 67067904, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65900544, + "loadedPeakRssBytes": 67969024, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 84283392, + "startupRssBytes": 64897024, + "loadedRssBytes": 68698112, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65888256, + "loadedPeakRssBytes": 69361664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedrooms-far-chest", + "solvers": { + "candidate": { + "peakRssBytes": 75325440, + "startupRssBytes": 65163264, + "loadedRssBytes": 66867200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66473984, + "loadedPeakRssBytes": 67735552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73637888, + "startupRssBytes": 65191936, + "loadedRssBytes": 67420160, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66863104, + "loadedPeakRssBytes": 68190208, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79294464, + "startupRssBytes": 67203072, + "loadedRssBytes": 67596288, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67887104, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75583488, + "startupRssBytes": 65593344, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66969600, + "loadedPeakRssBytes": 69156864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedrooms-near-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71790592, + "startupRssBytes": 64991232, + "loadedRssBytes": 66301952, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65740800, + "loadedPeakRssBytes": 67002368, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71270400, + "startupRssBytes": 64344064, + "loadedRssBytes": 67489792, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65359872, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79159296, + "startupRssBytes": 64802816, + "loadedRssBytes": 67293184, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66056192, + "loadedPeakRssBytes": 68067328, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 85274624, + "startupRssBytes": 65421312, + "loadedRssBytes": 68435968, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 69005312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-left-chest", + "solvers": { + "candidate": { + "peakRssBytes": 75079680, + "startupRssBytes": 65523712, + "loadedRssBytes": 66965504, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66568192, + "loadedPeakRssBytes": 67747840, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73498624, + "startupRssBytes": 65802240, + "loadedRssBytes": 67244032, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66772992, + "loadedPeakRssBytes": 67981312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79056896, + "startupRssBytes": 64958464, + "loadedRssBytes": 67186688, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65679360, + "loadedPeakRssBytes": 68022272, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 92065792, + "startupRssBytes": 64827392, + "loadedRssBytes": 68497408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66138112, + "loadedPeakRssBytes": 69206016, + "resourceUsageMaxRssBytes": 91205632, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-right-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72089600, + "startupRssBytes": 65638400, + "loadedRssBytes": 67211264, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67026944, + "loadedPeakRssBytes": 67960832, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72290304, + "startupRssBytes": 66674688, + "loadedRssBytes": 67592192, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67465216, + "loadedPeakRssBytes": 68386816, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79151104, + "startupRssBytes": 64765952, + "loadedRssBytes": 67256320, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65757184, + "loadedPeakRssBytes": 68038656, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 76038144, + "startupRssBytes": 65339392, + "loadedRssBytes": 68354048, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66514944, + "loadedPeakRssBytes": 69271552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor2-stairs-chest", + "solvers": { + "candidate": { + "peakRssBytes": 78045184, + "startupRssBytes": 64905216, + "loadedRssBytes": 66740224, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66195456, + "loadedPeakRssBytes": 67358720, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 75186176, + "startupRssBytes": 64901120, + "loadedRssBytes": 67260416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66289664, + "loadedPeakRssBytes": 68206592, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78721024, + "startupRssBytes": 64835584, + "loadedRssBytes": 67063808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66416640, + "loadedPeakRssBytes": 67735552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 93245440, + "startupRssBytes": 65855488, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66990080, + "loadedPeakRssBytes": 69308416, + "resourceUsageMaxRssBytes": 92598272, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor2-chest-2", + "solvers": { + "candidate": { + "peakRssBytes": 72044544, + "startupRssBytes": 65884160, + "loadedRssBytes": 67063808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67031040, + "loadedPeakRssBytes": 67891200, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72298496, + "startupRssBytes": 65290240, + "loadedRssBytes": 67256320, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66572288, + "loadedPeakRssBytes": 68222976, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78888960, + "startupRssBytes": 65081344, + "loadedRssBytes": 67178496, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66191360, + "loadedPeakRssBytes": 68038656, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74493952, + "startupRssBytes": 64372736, + "loadedRssBytes": 68567040, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65331200, + "loadedPeakRssBytes": 69373952, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor3-entrance-chest", + "solvers": { + "candidate": { + "peakRssBytes": 73695232, + "startupRssBytes": 65236992, + "loadedRssBytes": 66547712, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66519040, + "loadedPeakRssBytes": 67457024, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73441280, + "startupRssBytes": 65343488, + "loadedRssBytes": 67702784, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66543616, + "loadedPeakRssBytes": 68182016, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78643200, + "startupRssBytes": 65867776, + "loadedRssBytes": 67047424, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66863104, + "loadedPeakRssBytes": 67829760, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 76640256, + "startupRssBytes": 65904640, + "loadedRssBytes": 68657152, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66973696, + "loadedPeakRssBytes": 69349376, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor3-empty-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71852032, + "startupRssBytes": 66080768, + "loadedRssBytes": 66998272, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67137536, + "loadedPeakRssBytes": 67940352, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71245824, + "startupRssBytes": 65163264, + "loadedRssBytes": 67260416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66428928, + "loadedPeakRssBytes": 68218880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78536704, + "startupRssBytes": 65486848, + "loadedRssBytes": 66928640, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66781184, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75935744, + "startupRssBytes": 64512000, + "loadedRssBytes": 68575232, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65622016, + "loadedPeakRssBytes": 69304320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor4-elixirs-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72155136, + "startupRssBytes": 64491520, + "loadedRssBytes": 66719744, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65703936, + "loadedPeakRssBytes": 67670016, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71299072, + "startupRssBytes": 65552384, + "loadedRssBytes": 67387392, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 82427904, + "startupRssBytes": 65687552, + "loadedRssBytes": 66736128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66732032, + "loadedPeakRssBytes": 67620864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78761984, + "startupRssBytes": 65454080, + "loadedRssBytes": 68730880, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 69373952, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor4-food-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72327168, + "startupRssBytes": 64372736, + "loadedRssBytes": 65421312, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65400832, + "loadedPeakRssBytes": 66535424, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71421952, + "startupRssBytes": 65310720, + "loadedRssBytes": 67407872, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66527232, + "loadedPeakRssBytes": 68280320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79224832, + "startupRssBytes": 65339392, + "loadedRssBytes": 67174400, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66560000, + "loadedPeakRssBytes": 68120576, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 93716480, + "startupRssBytes": 65572864, + "loadedRssBytes": 67801088, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66535424, + "loadedPeakRssBytes": 68571136, + "resourceUsageMaxRssBytes": 93716480, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-top-chest", + "solvers": { + "candidate": { + "peakRssBytes": 79269888, + "startupRssBytes": 66072576, + "loadedRssBytes": 67121152, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67076096, + "loadedPeakRssBytes": 67948544, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 76902400, + "startupRssBytes": 65351680, + "loadedRssBytes": 67710976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66949120, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78548992, + "startupRssBytes": 65146880, + "loadedRssBytes": 66981888, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 67817472, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 86102016, + "startupRssBytes": 65794048, + "loadedRssBytes": 68546560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66965504, + "loadedPeakRssBytes": 69246976, + "resourceUsageMaxRssBytes": 85250048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "fire-lizard-cave-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71999488, + "startupRssBytes": 65421312, + "loadedRssBytes": 67125248, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66793472, + "loadedPeakRssBytes": 67944448, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71417856, + "startupRssBytes": 64843776, + "loadedRssBytes": 67334144, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66064384, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79163392, + "startupRssBytes": 65540096, + "loadedRssBytes": 66850816, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66707456, + "loadedPeakRssBytes": 67694592, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 94773248, + "startupRssBytes": 65036288, + "loadedRssBytes": 68706304, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66277376, + "loadedPeakRssBytes": 69341184, + "resourceUsageMaxRssBytes": 94773248, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "silas-tavern-basement-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72122368, + "startupRssBytes": 64565248, + "loadedRssBytes": 66269184, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65400832, + "loadedPeakRssBytes": 66957312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71405568, + "startupRssBytes": 65630208, + "loadedRssBytes": 67727360, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66318336, + "loadedPeakRssBytes": 68210688, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78974976, + "startupRssBytes": 65765376, + "loadedRssBytes": 67600384, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67031040, + "loadedPeakRssBytes": 68091904, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74752000, + "startupRssBytes": 65855488, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67010560, + "loadedPeakRssBytes": 69316608, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "silas-tavern-storeroom-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71946240, + "startupRssBytes": 66117632, + "loadedRssBytes": 67166208, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67235840, + "loadedPeakRssBytes": 67985408, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71139328, + "startupRssBytes": 65794048, + "loadedRssBytes": 67760128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67022848, + "loadedPeakRssBytes": 68370432, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79196160, + "startupRssBytes": 65155072, + "loadedRssBytes": 67252224, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66383872, + "loadedPeakRssBytes": 68141056, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78635008, + "startupRssBytes": 64790528, + "loadedRssBytes": 68329472, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66011136, + "loadedPeakRssBytes": 69222400, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "silas-tavern-storeroom-door", + "solvers": { + "candidate": { + "peakRssBytes": 72241152, + "startupRssBytes": 64372736, + "loadedRssBytes": 65290240, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65490944, + "loadedPeakRssBytes": 66146304, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71512064, + "startupRssBytes": 64557056, + "loadedRssBytes": 67440640, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65273856, + "loadedPeakRssBytes": 68132864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78893056, + "startupRssBytes": 65040384, + "loadedRssBytes": 67399680, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66592768, + "loadedPeakRssBytes": 68124672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75194368, + "startupRssBytes": 65142784, + "loadedRssBytes": 68419584, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66314240, + "loadedPeakRssBytes": 69242880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "jan-pond-house-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71491584, + "startupRssBytes": 65925120, + "loadedRssBytes": 66842624, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66760704, + "loadedPeakRssBytes": 67538944, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70500352, + "startupRssBytes": 65695744, + "loadedRssBytes": 67137536, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66633728, + "loadedPeakRssBytes": 67809280, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78970880, + "startupRssBytes": 65470464, + "loadedRssBytes": 67436544, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66777088, + "loadedPeakRssBytes": 68116480, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 93093888, + "startupRssBytes": 65413120, + "loadedRssBytes": 68034560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66576384, + "loadedPeakRssBytes": 68816896, + "resourceUsageMaxRssBytes": 91906048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "graham-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72069120, + "startupRssBytes": 64765952, + "loadedRssBytes": 66338816, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65581056, + "loadedPeakRssBytes": 67194880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71299072, + "startupRssBytes": 65204224, + "loadedRssBytes": 67563520, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66428928, + "loadedPeakRssBytes": 68218880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78872576, + "startupRssBytes": 65208320, + "loadedRssBytes": 67305472, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66457600, + "loadedPeakRssBytes": 68055040, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 79687680, + "startupRssBytes": 65462272, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66789376, + "loadedPeakRssBytes": 69304320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gravo-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72118272, + "startupRssBytes": 64983040, + "loadedRssBytes": 66686976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66228224, + "loadedPeakRssBytes": 67579904, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70488064, + "startupRssBytes": 65179648, + "loadedRssBytes": 66883584, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66408448, + "loadedPeakRssBytes": 67633152, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79040512, + "startupRssBytes": 65675264, + "loadedRssBytes": 67117056, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66781184, + "loadedPeakRssBytes": 67846144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75771904, + "startupRssBytes": 65347584, + "loadedRssBytes": 68624384, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66322432, + "loadedPeakRssBytes": 69361664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "diego-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72146944, + "startupRssBytes": 65880064, + "loadedRssBytes": 67190784, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67063808, + "loadedPeakRssBytes": 68005888, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72073216, + "startupRssBytes": 65470464, + "loadedRssBytes": 67567616, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66408448, + "loadedPeakRssBytes": 68186112, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78852096, + "startupRssBytes": 65925120, + "loadedRssBytes": 67366912, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67059712, + "loadedPeakRssBytes": 68120576, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78807040, + "startupRssBytes": 65200128, + "loadedRssBytes": 68345856, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66367488, + "loadedPeakRssBytes": 69296128, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "grim-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72241152, + "startupRssBytes": 65159168, + "loadedRssBytes": 66994176, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66891776, + "loadedPeakRssBytes": 67862528, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71405568, + "startupRssBytes": 65433600, + "loadedRssBytes": 67399680, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66494464, + "loadedPeakRssBytes": 68214784, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78860288, + "startupRssBytes": 65060864, + "loadedRssBytes": 67158016, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66310144, + "loadedPeakRssBytes": 68034560, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75022336, + "startupRssBytes": 65683456, + "loadedRssBytes": 68567040, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 69443584, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "mercenary-near-grim-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71467008, + "startupRssBytes": 65024000, + "loadedRssBytes": 66072576, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66228224, + "loadedPeakRssBytes": 66879488, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71958528, + "startupRssBytes": 65830912, + "loadedRssBytes": 67403776, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66977792, + "loadedPeakRssBytes": 68313088, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78442496, + "startupRssBytes": 65724416, + "loadedRssBytes": 66904064, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66744320, + "loadedPeakRssBytes": 67756032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78852096, + "startupRssBytes": 64790528, + "loadedRssBytes": 68591616, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65409024, + "loadedPeakRssBytes": 69369856, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "popular-lock", + "solvers": { + "candidate": { + "peakRssBytes": 72925184, + "startupRssBytes": 64864256, + "loadedRssBytes": 67223552, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66072576, + "loadedPeakRssBytes": 67854336, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72818688, + "startupRssBytes": 65572864, + "loadedRssBytes": 67670016, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66842624, + "loadedPeakRssBytes": 68358144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78925824, + "startupRssBytes": 64843776, + "loadedRssBytes": 67465216, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66355200, + "loadedPeakRssBytes": 68124672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75907072, + "startupRssBytes": 65937408, + "loadedRssBytes": 67903488, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66772992, + "loadedPeakRssBytes": 68808704, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "alberto-mine-hut-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72032256, + "startupRssBytes": 65372160, + "loadedRssBytes": 66945024, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66519040, + "loadedPeakRssBytes": 67907584, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71299072, + "startupRssBytes": 66199552, + "loadedRssBytes": 67772416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67170304, + "loadedPeakRssBytes": 68464640, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78888960, + "startupRssBytes": 65069056, + "loadedRssBytes": 67166208, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66940928, + "loadedPeakRssBytes": 68124672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75685888, + "startupRssBytes": 65064960, + "loadedRssBytes": 68472832, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66490368, + "loadedPeakRssBytes": 69316608, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "alberto-mine-upstairs-left-chest", + "solvers": { + "candidate": { + "peakRssBytes": 70963200, + "startupRssBytes": 65175552, + "loadedRssBytes": 67010560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66551808, + "loadedPeakRssBytes": 67665920, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71032832, + "startupRssBytes": 65646592, + "loadedRssBytes": 67481600, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68358144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 71507968, + "startupRssBytes": 65769472, + "loadedRssBytes": 67080192, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66801664, + "loadedPeakRssBytes": 67776512, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 72077312, + "startupRssBytes": 65044480, + "loadedRssBytes": 68321280, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66244608, + "loadedPeakRssBytes": 69312512, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "alberto-mine-upstairs-right-chest", + "solvers": { + "candidate": { + "peakRssBytes": 70955008, + "startupRssBytes": 65347584, + "loadedRssBytes": 66658304, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 67461120, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70983680, + "startupRssBytes": 65761280, + "loadedRssBytes": 67334144, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66994176, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78843904, + "startupRssBytes": 65380352, + "loadedRssBytes": 67477504, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66445312, + "loadedPeakRssBytes": 68112384, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 73433088, + "startupRssBytes": 65761280, + "loadedRssBytes": 67727360, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66560000, + "loadedPeakRssBytes": 68489216, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "cavalorn-storeroom-door", + "solvers": { + "candidate": { + "peakRssBytes": 72126464, + "startupRssBytes": 65568768, + "loadedRssBytes": 67010560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66990080, + "loadedPeakRssBytes": 67907584, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71397376, + "startupRssBytes": 65282048, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66547712, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79187968, + "startupRssBytes": 65269760, + "loadedRssBytes": 67366912, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66609152, + "loadedPeakRssBytes": 68096000, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 85778432, + "startupRssBytes": 65150976, + "loadedRssBytes": 68558848, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66166784, + "loadedPeakRssBytes": 69103616, + "resourceUsageMaxRssBytes": 85106688, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "old-to-new-camp-bandits-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72785920, + "startupRssBytes": 65982464, + "loadedRssBytes": 67162112, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67121152, + "loadedPeakRssBytes": 67997696, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71737344, + "startupRssBytes": 65581056, + "loadedRssBytes": 67416064, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66609152, + "loadedPeakRssBytes": 68075520, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79044608, + "startupRssBytes": 65212416, + "loadedRssBytes": 67309568, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66351104, + "loadedPeakRssBytes": 68112384, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74752000, + "startupRssBytes": 65073152, + "loadedRssBytes": 68218880, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66187264, + "loadedPeakRssBytes": 69152768, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + } + ] + }, + "config": { + "repetitions": 7, + "warmups": 2, + "lockCount": 45, + "scope": "full-45-catalog", + "timingBoundary": "Only synchronous public solver call; cloning, legacy/reference normalization, replay and metrics excluded.", + "order": "Per-lock baseline-vs-itself calibration first; matched rounds rotate by lock and round. A non-pass receives one bounded repeat with all raw attempts retained.", + "uncertainty": "Paired log-ratio mean with conservative 2.776 standard-error envelope; control sets tolerance. >=5 samples; >25% control envelope cannot certify a pass.", + "gc": "Natural GC; no forced collection.", + "fixtureSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675" + }, + "aggregates": { + "originalBfs": { + "A": 483, + "U": 245, + "C": 1727, + "plateSwitches": 438, + "sumOfMediansMs": 2653.7006490000126 + }, + "upstream": { + "A": 491, + "U": 245, + "C": 1695, + "plateSwitches": 446, + "sumOfMediansMs": 1517.4034640000011 + }, + "candidate": { + "A": 483, + "U": 245, + "C": 1723, + "plateSwitches": 438, + "sumOfMediansMs": 47.3627580000109 + }, + "baseline": { + "A": 483, + "U": 245, + "C": 1723, + "plateSwitches": 438, + "sumOfMediansMs": 48.507443999980296 + } + }, + "reports": { + "json": { + "path": "benchmark.json", + "sha256": "066f75d89ae450a2e0697d0e94b37864adbc81c6ff0e902eda3bea7a38e9ed81" + }, + "markdown": { + "path": "benchmark.md", + "sha256": "2b3926535fc7c0a385a4e3d5e5d89b5367e93359dd0a76b8dcf8539315e2e2ba" + } + } +} diff --git a/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.json b/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.json new file mode 100644 index 0000000..08ad7f9 --- /dev/null +++ b/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.json @@ -0,0 +1,25093 @@ +{ + "schemaVersion": 1, + "createdAt": "2026-09-07T09:12:21.367Z", + "config": { + "repetitions": 7, + "warmups": 2, + "lockCount": 45, + "scope": "full-45-catalog", + "timingBoundary": "Only synchronous public solver call; cloning, legacy/reference normalization, replay and metrics excluded.", + "order": "Per-lock baseline-vs-itself calibration first; matched rounds rotate by lock and round. A non-pass receives one bounded repeat with all raw attempts retained.", + "uncertainty": "Paired log-ratio mean with conservative 2.776 standard-error envelope; control sets tolerance. >=5 samples; >25% control envelope cannot certify a pass.", + "gc": "Natural GC; no forced collection.", + "fixtureSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675" + }, + "correctness": { + "passed": true + }, + "quality": "passed", + "performance": "inconclusive", + "implementations": { + "originalBfs": "Original Apache BFS", + "upstream": "UnlockMyLoot pinned external reference", + "candidate": "Candidate TypeScript library", + "baseline": "Target/reference baseline" + }, + "aggregates": { + "originalBfs": { + "A": 483, + "U": 245, + "C": 1727, + "plateSwitches": 438, + "sumOfMediansMs": 2653.7006490000126 + }, + "upstream": { + "A": 491, + "U": 245, + "C": 1695, + "plateSwitches": 446, + "sumOfMediansMs": 1517.4034640000011 + }, + "candidate": { + "A": 483, + "U": 245, + "C": 1723, + "plateSwitches": 438, + "sumOfMediansMs": 47.3627580000109 + }, + "baseline": { + "A": 483, + "U": 245, + "C": 1723, + "plateSwitches": 438, + "sumOfMediansMs": 48.507443999980296 + } + }, + "results": [ + { + "id": "ruined-tower-door", + "title": "Ruined Tower door", + "optimumA": 17, + "solvers": { + "originalBfs": { + "A": 17, + "U": 6, + "C": 57, + "plateSwitches": 16, + "commands": [ + [ + 0, + -4 + ], + [ + 2, + 5 + ], + [ + 3, + -6 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 5, + -2 + ], + [ + 0, + -4 + ], + [ + 1, + 3 + ], + [ + 3, + -5 + ], + [ + 4, + -3 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 1, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 93.975911, + 79.51385199999993, + 252.6963740000001, + 97.26116200000001, + 89.65834799999993, + 85.219783, + 86.95025499999997 + ], + "timingMs": { + "min": 79.51385199999993, + "median": 89.65834799999993, + "p95": 252.6963740000001, + "max": 252.6963740000001 + } + }, + "upstream": { + "A": 17, + "U": 6, + "C": 57, + "plateSwitches": 16, + "commands": [ + [ + 2, + 5 + ], + [ + 3, + -5 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 0, + -5 + ], + [ + 5, + -2 + ], + [ + 1, + 3 + ], + [ + 4, + -3 + ], + [ + 0, + -6 + ], + [ + 5, + -3 + ], + [ + 3, + -6 + ], + [ + 1, + 6 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 132.731356, + 82.08698200000003, + 99.24884099999986, + 93.8309240000001, + 94.43956899999989, + 91.279086, + 80.26694399999997 + ], + "timingMs": { + "min": 80.26694399999997, + "median": 93.8309240000001, + "p95": 132.731356, + "max": 132.731356 + } + }, + "candidate": { + "A": 17, + "U": 6, + "C": 57, + "plateSwitches": 16, + "commands": [ + [ + 0, + -4 + ], + [ + 2, + 5 + ], + [ + 3, + -6 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 5, + -2 + ], + [ + 0, + -4 + ], + [ + 1, + 3 + ], + [ + 3, + -5 + ], + [ + 4, + -3 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 1, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 4.910295000000019, + 3.461626000000024, + 4.107326999999941, + 3.5131129999999757, + 3.577199000000064, + 3.8338260000000446, + 3.4595429999999396 + ], + "timingMs": { + "min": 3.4595429999999396, + "median": 3.577199000000064, + "p95": 4.910295000000019, + "max": 4.910295000000019 + } + }, + "baseline": { + "A": 17, + "U": 6, + "C": 57, + "plateSwitches": 16, + "commands": [ + [ + 0, + -4 + ], + [ + 2, + 5 + ], + [ + 3, + -6 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 5, + -2 + ], + [ + 0, + -4 + ], + [ + 1, + 3 + ], + [ + 3, + -5 + ], + [ + 4, + -3 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 1, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 3.508687000000009, + 5.843217999999979, + 3.7145660000001044, + 3.319001999999955, + 3.333893999999873, + 5.0080820000000585, + 3.165269999999964 + ], + "timingMs": { + "min": 3.165269999999964, + "median": 3.508687000000009, + "p95": 5.843217999999979, + "max": 5.843217999999979 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.9804851261710085, + "noiseRatio": 1.357658308620493, + "lowerRatio": 0.7282804758163847, + "upperRatio": 1.3200286353481152 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9804851261710085, + "noiseRatio": 1.357658308620493, + "lowerRatio": 0.7282804758163847, + "upperRatio": 1.3200286353481152, + "candidate": [ + 4.910295000000019, + 3.461626000000024, + 4.107326999999941, + 3.5131129999999757, + 3.577199000000064, + 3.8338260000000446, + 3.4595429999999396 + ], + "baseline": [ + 3.508687000000009, + 5.843217999999979, + 3.7145660000001044, + 3.319001999999955, + 3.333893999999873, + 5.0080820000000585, + 3.165269999999964 + ], + "selfA": [ + 3.5670550000000105, + 6.0774000000000115, + 3.186813000000001, + 3.5614960000000053, + 4.496512999999993, + 5.546760999999975, + 4.151252999999997 + ], + "selfB": [ + 5.820503000000002, + 5.289063999999996, + 3.3500589999999875, + 3.5607949999999846, + 3.0661910000000034, + 5.378298999999998, + 3.537520999999998 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.8903081529797303, + "noiseRatio": 1.419399645461027, + "lowerRatio": 0.5937780456252367, + "upperRatio": 1.334924073232676, + "candidate": [ + 3.3646300000000338, + 3.1216850000000704, + 3.7707299999997304, + 2.944879000000128, + 6.317911999999978, + 3.1307779999997365, + 3.08801399999993 + ], + "baseline": [ + 3.139651999999842, + 3.1827769999999873, + 3.1163060000003497, + 7.7678820000001, + 6.822991000000002, + 3.0908190000000104, + 3.14965600000005 + ], + "selfA": [ + 3.6906999999996515, + 3.0568869999997332, + 3.35890100000006, + 4.635651999999936, + 3.040171999999984, + 5.106238999999732, + 3.375757000000249 + ], + "selfB": [ + 3.215625999999702, + 3.1390299999998206, + 6.096318000000338, + 3.0291560000000572, + 3.0314589999998134, + 4.34230000000025, + 3.183157000000392 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 3.5670550000000105, + 6.0774000000000115, + 3.186813000000001, + 3.5614960000000053, + 4.496512999999993, + 5.546760999999975, + 4.151252999999997 + ], + "baselineSelfB": [ + 5.820503000000002, + 5.289063999999996, + 3.3500589999999875, + 3.5607949999999846, + 3.0661910000000034, + 5.378298999999998, + 3.537520999999998 + ] + }, + "historicalReference": { + "A": 17, + "U": 6, + "C": 57, + "plateSwitches": 16, + "commands": [ + [ + 2, + 5 + ], + [ + 3, + -5 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 1, + 1 + ], + [ + 4, + -1 + ], + [ + 0, + -5 + ], + [ + 5, + -2 + ], + [ + 1, + 3 + ], + [ + 4, + -3 + ], + [ + 0, + -6 + ], + [ + 5, + -3 + ], + [ + 3, + -6 + ], + [ + 1, + 6 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "scatty-chest", + "title": "Scatty's chest", + "optimumA": 9, + "solvers": { + "originalBfs": { + "A": 9, + "U": 5, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 2, + 4 + ], + [ + 4, + 6 + ], + [ + 3, + 5 + ], + [ + 0, + -2 + ], + [ + 4, + 5 + ], + [ + 1, + -2 + ], + [ + 2, + 3 + ], + [ + 4, + 2 + ] + ], + "samplesMs": [ + 33.7264110000001, + 12.547288999999637, + 14.25079199999982, + 11.793896000000132, + 11.134845000000041, + 12.145755999999892, + 12.405906999999843 + ], + "timingMs": { + "min": 11.134845000000041, + "median": 12.405906999999843, + "p95": 33.7264110000001, + "max": 33.7264110000001 + } + }, + "upstream": { + "A": 9, + "U": 5, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + 5 + ], + [ + 1, + -1 + ], + [ + 4, + 6 + ], + [ + 3, + 5 + ], + [ + 4, + 5 + ], + [ + 2, + 2 + ], + [ + 4, + 2 + ], + [ + 1, + -3 + ], + [ + 0, + -2 + ] + ], + "samplesMs": [ + 6.514267000000018, + 5.445058000000245, + 5.767013000000134, + 5.647273000000041, + 5.285999000000174, + 5.755354999999781, + 5.33290100000022 + ], + "timingMs": { + "min": 5.285999000000174, + "median": 5.647273000000041, + "p95": 6.514267000000018, + "max": 6.514267000000018 + } + }, + "candidate": { + "A": 9, + "U": 5, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + 5 + ], + [ + 4, + 5 + ], + [ + 1, + -1 + ], + [ + 3, + 5 + ], + [ + 4, + 2 + ], + [ + 0, + -2 + ], + [ + 2, + 2 + ], + [ + 4, + 6 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 0.14935400000013033, + 0.13378099999999904, + 0.1606019999999262, + 0.13623499999994237, + 0.1360740000000078, + 0.1368959999999788, + 0.16545900000028269 + ], + "timingMs": { + "min": 0.13378099999999904, + "median": 0.1368959999999788, + "p95": 0.16545900000028269, + "max": 0.16545900000028269 + } + }, + "baseline": { + "A": 9, + "U": 5, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + 5 + ], + [ + 4, + 5 + ], + [ + 1, + -1 + ], + [ + 3, + 5 + ], + [ + 4, + 2 + ], + [ + 0, + -2 + ], + [ + 2, + 2 + ], + [ + 4, + 6 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 0.1980679999996937, + 0.10514899999998306, + 0.1159349999998085, + 0.1378669999999147, + 0.11276999999972759, + 0.18676099999993312, + 0.14965499999971144 + ], + "timingMs": { + "min": 0.10514899999998306, + "median": 0.1378669999999147, + "p95": 0.1980679999996937, + "max": 0.1980679999996937 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.0363829747101125, + "noiseRatio": 2.0246954191039723, + "lowerRatio": 0.7967228074032943, + "upperRatio": 1.348134709196654 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0363829747101125, + "noiseRatio": 2.0246954191039723, + "lowerRatio": 0.7967228074032943, + "upperRatio": 1.348134709196654, + "candidate": [ + 0.14935400000013033, + 0.13378099999999904, + 0.1606019999999262, + 0.13623499999994237, + 0.1360740000000078, + 0.1368959999999788, + 0.16545900000028269 + ], + "baseline": [ + 0.1980679999996937, + 0.10514899999998306, + 0.1159349999998085, + 0.1378669999999147, + 0.11276999999972759, + 0.18676099999993312, + 0.14965499999971144 + ], + "selfA": [ + 0.11393099999986589, + 0.06770199999982651, + 0.06397599999991144, + 0.062163000000055035, + 0.07027599999992162, + 0.06251400000019203, + 0.07274899999993067 + ], + "selfB": [ + 0.0724289999998291, + 0.06700099999989106, + 0.0647369999996954, + 0.06660000000010768, + 0.062022999999953754, + 0.06314499999962209, + 0.273330999999871 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.4873576036137215, + "noiseRatio": 1.4780907145708593, + "lowerRatio": 1.131960417757932, + "upperRatio": 1.9543374541393506, + "candidate": [ + 0.1886939999999413, + 0.16295599999966726, + 0.13485300000002098, + 0.20311499999979787, + 0.08927399999993213, + 0.09652599999981248, + 0.08987500000012005 + ], + "baseline": [ + 0.1297150000000329, + 0.12473699999964083, + 0.07942899999989095, + 0.08088200000020151, + 0.07300900000018373, + 0.07023499999968408, + 0.07599500000014814 + ], + "selfA": [ + 0.12841299999990952, + 0.21343100000012782, + 0.08633999999983644, + 0.07011599999987084, + 0.07784700000001976, + 0.06522799999993367, + 0.06732099999999264 + ], + "selfB": [ + 0.12769200000002456, + 0.11864900000000489, + 0.07062600000017483, + 0.0658490000000711, + 0.07428200000003926, + 0.11194900000009511, + 0.0753629999999248 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.11393099999986589, + 0.06770199999982651, + 0.06397599999991144, + 0.062163000000055035, + 0.07027599999992162, + 0.06251400000019203, + 0.07274899999993067 + ], + "baselineSelfB": [ + 0.0724289999998291, + 0.06700099999989106, + 0.0647369999996954, + 0.06660000000010768, + 0.062022999999953754, + 0.06314499999962209, + 0.273330999999871 + ] + }, + "historicalReference": { + "A": 9, + "U": 5, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + 5 + ], + [ + 1, + -1 + ], + [ + 4, + 6 + ], + [ + 3, + 5 + ], + [ + 4, + 5 + ], + [ + 2, + 2 + ], + [ + 4, + 2 + ], + [ + 1, + -3 + ], + [ + 0, + -2 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "cor-galom-chest", + "title": "Cor Galom's chest", + "optimumA": 5, + "solvers": { + "originalBfs": { + "A": 5, + "U": 4, + "C": 19, + "plateSwitches": 4, + "commands": [ + [ + 3, + 6 + ], + [ + 1, + -5 + ], + [ + 0, + 4 + ], + [ + 2, + -2 + ], + [ + 3, + -2 + ] + ], + "samplesMs": [ + 0.5159859999998844, + 0.5061309999996411, + 0.4506079999996473, + 0.5328309999999874, + 0.45438300000023446, + 0.49082799999996496, + 0.5519100000001345 + ], + "timingMs": { + "min": 0.4506079999996473, + "median": 0.5061309999996411, + "p95": 0.5519100000001345, + "max": 0.5519100000001345 + } + }, + "upstream": { + "A": 5, + "U": 4, + "C": 15, + "plateSwitches": 4, + "commands": [ + [ + 3, + 4 + ], + [ + 1, + -4 + ], + [ + 2, + -2 + ], + [ + 0, + 4 + ], + [ + 1, + -1 + ] + ], + "samplesMs": [ + 0.33120800000006057, + 0.3216040000002067, + 0.427672999999686, + 0.3339730000002419, + 0.34301500000037777, + 0.4625960000003033, + 0.37169900000026246 + ], + "timingMs": { + "min": 0.3216040000002067, + "median": 0.34301500000037777, + "p95": 0.4625960000003033, + "max": 0.4625960000003033 + } + }, + "candidate": { + "A": 5, + "U": 4, + "C": 15, + "plateSwitches": 4, + "commands": [ + [ + 3, + 4 + ], + [ + 1, + -4 + ], + [ + 0, + 4 + ], + [ + 1, + -1 + ], + [ + 2, + -2 + ] + ], + "samplesMs": [ + 0.09535400000004302, + 0.06094100000018443, + 0.04940499999975145, + 0.054341999999905966, + 0.1158940000000257, + 0.08681000000024142, + 0.08066099999996368 + ], + "timingMs": { + "min": 0.04940499999975145, + "median": 0.08066099999996368, + "p95": 0.1158940000000257, + "max": 0.1158940000000257 + } + }, + "baseline": { + "A": 5, + "U": 4, + "C": 15, + "plateSwitches": 4, + "commands": [ + [ + 3, + 4 + ], + [ + 1, + -4 + ], + [ + 0, + 4 + ], + [ + 1, + -1 + ], + [ + 2, + -2 + ] + ], + "samplesMs": [ + 0.04224400000020978, + 0.03475200000002587, + 0.03346000000010463, + 0.04046100000005026, + 0.057396000000153435, + 0.03976999999986219, + 0.133661000000302 + ], + "timingMs": { + "min": 0.03346000000010463, + "median": 0.04046100000005026, + "p95": 0.133661000000302, + "max": 0.133661000000302 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.5435779764492534, + "noiseRatio": 1.8234296600891973, + "lowerRatio": 0.9550433290368372, + "upperRatio": 2.4947904424211425 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.5435779764492534, + "noiseRatio": 1.8234296600891973, + "lowerRatio": 0.9550433290368372, + "upperRatio": 2.4947904424211425, + "candidate": [ + 0.09535400000004302, + 0.06094100000018443, + 0.04940499999975145, + 0.054341999999905966, + 0.1158940000000257, + 0.08681000000024142, + 0.08066099999996368 + ], + "baseline": [ + 0.04224400000020978, + 0.03475200000002587, + 0.03346000000010463, + 0.04046100000005026, + 0.057396000000153435, + 0.03976999999986219, + 0.133661000000302 + ], + "selfA": [ + 0.025908999999955995, + 0.06651000000010754, + 0.02683999999999287, + 0.02515799999991941, + 0.022163999999975204, + 0.025247999999919557, + 0.022052999999687017 + ], + "selfB": [ + 0.025328000000172324, + 0.023044000000027154, + 0.022883999999976368, + 0.02295400000002701, + 0.022202999999990425, + 0.02392599999984668, + 0.02228400000012698 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0216255775528418, + "noiseRatio": 2.6635975805854497, + "lowerRatio": 0.5502563143255477, + "upperRatio": 1.8967866311347457, + "candidate": [ + 0.028073000000404136, + 0.03722600000037346, + 0.03188799999998082, + 0.032999999999901775, + 0.031497000000399566, + 0.036203999999997905, + 0.03204800000003161 + ], + "baseline": [ + 0.02642999999989115, + 0.023034999999708816, + 0.05350999999973283, + 0.09242899999981091, + 0.024636999999984255, + 0.01955000000043583, + 0.02385599999979604 + ], + "selfA": [ + 0.08245399999987058, + 0.02698000000009415, + 0.044156999999813706, + 0.031568000000333996, + 0.02613999999994121, + 0.06915399999979854, + 0.021812999999838212 + ], + "selfB": [ + 0.02602999999999156, + 0.05736700000034034, + 0.02724099999977625, + 0.023154999999860593, + 0.03159799999957613, + 0.025989000000208762, + 0.02250400000002628 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.025908999999955995, + 0.06651000000010754, + 0.02683999999999287, + 0.02515799999991941, + 0.022163999999975204, + 0.025247999999919557, + 0.022052999999687017 + ], + "baselineSelfB": [ + 0.025328000000172324, + 0.023044000000027154, + 0.022883999999976368, + 0.02295400000002701, + 0.022202999999990425, + 0.02392599999984668, + 0.02228400000012698 + ] + }, + "historicalReference": { + "A": 5, + "U": 4, + "C": 15, + "plateSwitches": 4, + "commands": [ + [ + 3, + 4 + ], + [ + 1, + -4 + ], + [ + 2, + -2 + ], + [ + 0, + 4 + ], + [ + 1, + -1 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": -4, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "cor-galom-bedroom-chest", + "title": "Cor Galom's bedroom chest", + "optimumA": 14, + "solvers": { + "originalBfs": { + "A": 14, + "U": 5, + "C": 60, + "plateSwitches": 13, + "commands": [ + [ + 0, + 1 + ], + [ + 1, + -5 + ], + [ + 2, + -5 + ], + [ + 1, + -5 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 3, + 5 + ], + [ + 2, + -5 + ], + [ + 0, + 4 + ], + [ + 1, + -5 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 2, + -3 + ], + [ + 3, + 2 + ] + ], + "samplesMs": [ + 78.03713100000004, + 75.95942600000035, + 70.7323849999998, + 69.93971300000021, + 71.83849899999996, + 70.13433600000008, + 71.32951400000002 + ], + "timingMs": { + "min": 69.93971300000021, + "median": 71.32951400000002, + "p95": 78.03713100000004, + "max": 78.03713100000004 + } + }, + "upstream": { + "A": 14, + "U": 5, + "C": 60, + "plateSwitches": 13, + "commands": [ + [ + 1, + -3 + ], + [ + 2, + -4 + ], + [ + 1, + -4 + ], + [ + 4, + -4 + ], + [ + 3, + 4 + ], + [ + 1, + -6 + ], + [ + 0, + 2 + ], + [ + 2, + -6 + ], + [ + 1, + -6 + ], + [ + 4, + -6 + ], + [ + 3, + 3 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 32.70353299999988, + 32.28175400000009, + 30.35910799999965, + 30.02255100000002, + 32.019856000000345, + 30.818619000000126, + 32.880349000000024 + ], + "timingMs": { + "min": 30.02255100000002, + "median": 32.019856000000345, + "p95": 32.880349000000024, + "max": 32.880349000000024 + } + }, + "candidate": { + "A": 14, + "U": 5, + "C": 60, + "plateSwitches": 13, + "commands": [ + [ + 1, + -5 + ], + [ + 2, + -4 + ], + [ + 1, + -4 + ], + [ + 4, + -4 + ], + [ + 1, + -4 + ], + [ + 3, + 4 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 2, + -2 + ], + [ + 1, + -6 + ], + [ + 4, + -6 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.2727210000002742, + 0.23813799999970797, + 0.2338009999998576, + 0.23570500000005268, + 0.26649100000031467, + 0.32682199999999284, + 0.21878900000001522 + ], + "timingMs": { + "min": 0.21878900000001522, + "median": 0.23813799999970797, + "p95": 0.32682199999999284, + "max": 0.32682199999999284 + } + }, + "baseline": { + "A": 14, + "U": 5, + "C": 60, + "plateSwitches": 13, + "commands": [ + [ + 1, + -5 + ], + [ + 2, + -4 + ], + [ + 1, + -4 + ], + [ + 4, + -4 + ], + [ + 1, + -4 + ], + [ + 3, + 4 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 2, + -2 + ], + [ + 1, + -6 + ], + [ + 4, + -6 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.22145300000011048, + 0.197367000000213, + 0.22184400000014648, + 0.1985580000000482, + 0.19563400000015463, + 0.19846899999993184, + 0.21496300000035262 + ], + "timingMs": { + "min": 0.19563400000015463, + "median": 0.1985580000000482, + "p95": 0.22184400000014648, + "max": 0.22184400000014648 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.2293746761073383, + "noiseRatio": 1.2779273712488457, + "lowerRatio": 1.0375119695781043, + "upperRatio": 1.4567177426093751 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.2293746761073383, + "noiseRatio": 1.2779273712488457, + "lowerRatio": 1.0375119695781043, + "upperRatio": 1.4567177426093751, + "candidate": [ + 0.2727210000002742, + 0.23813799999970797, + 0.2338009999998576, + 0.23570500000005268, + 0.26649100000031467, + 0.32682199999999284, + 0.21878900000001522 + ], + "baseline": [ + 0.22145300000011048, + 0.197367000000213, + 0.22184400000014648, + 0.1985580000000482, + 0.19563400000015463, + 0.19846899999993184, + 0.21496300000035262 + ], + "selfA": [ + 0.18691100000023653, + 0.1442469999997229, + 0.1282629999996061, + 0.12647000000015396, + 0.118228000000272, + 0.20340600000008635, + 0.1367259999997259 + ], + "selfB": [ + 0.16006099999958678, + 0.13411199999973178, + 0.16986500000029991, + 0.1402709999997569, + 0.11820799999986775, + 0.1480229999997391, + 0.11977999999999156 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.2045595890169252, + "noiseRatio": 1.781776198521705, + "lowerRatio": 0.9743331292483921, + "upperRatio": 1.4891865625177991, + "candidate": [ + 0.1674009999996997, + 0.21000599999979386, + 0.17137800000000425, + 0.17270000000007713, + 0.20803299999988667, + 0.17121699999961493, + 0.19240999999965425 + ], + "baseline": [ + 0.14692100000002029, + 0.11700599999994665, + 0.17968999999993684, + 0.1667909999996482, + 0.1608219999998255, + 0.1455789999999979, + 0.16066200000022945 + ], + "selfA": [ + 0.13042700000005425, + 0.35805900000013935, + 0.20953500000041458, + 0.1471510000001217, + 0.1368649999999434, + 0.22306600000001708, + 0.13616499999989173 + ], + "selfB": [ + 0.14216399999986606, + 0.16440700000021025, + 0.18494800000007672, + 0.2198910000001888, + 0.1268910000003416, + 0.12908400000014808, + 0.11905900000010661 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.18691100000023653, + 0.1442469999997229, + 0.1282629999996061, + 0.12647000000015396, + 0.118228000000272, + 0.20340600000008635, + 0.1367259999997259 + ], + "baselineSelfB": [ + 0.16006099999958678, + 0.13411199999973178, + 0.16986500000029991, + 0.1402709999997569, + 0.11820799999986775, + 0.1480229999997391, + 0.11977999999999156 + ] + }, + "historicalReference": { + "A": 14, + "U": 5, + "C": 60, + "plateSwitches": 13, + "commands": [ + [ + 1, + -3 + ], + [ + 2, + -4 + ], + [ + 1, + -4 + ], + [ + 4, + -4 + ], + [ + 3, + 4 + ], + [ + 1, + -6 + ], + [ + 0, + 2 + ], + [ + 2, + -6 + ], + [ + 1, + -6 + ], + [ + 4, + -6 + ], + [ + 3, + 3 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 0, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "swamp-camp-forge-chest", + "title": "Swamp Camp forge chest", + "optimumA": 11, + "solvers": { + "originalBfs": { + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 2, + 6 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + -5 + ], + [ + 2, + 5 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 1, + -1 + ], + [ + 2, + 5 + ], + [ + 3, + -3 + ], + [ + 4, + -2 + ] + ], + "samplesMs": [ + 106.38693699999976, + 104.2116259999998, + 105.29213999999956, + 120.80871600000046, + 106.04047600000013, + 107.70715899999959, + 107.91578700000082 + ], + "timingMs": { + "min": 104.2116259999998, + "median": 106.38693699999976, + "p95": 120.80871600000046, + "max": 120.80871600000046 + } + }, + "upstream": { + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 2, + 5 + ], + [ + 3, + -5 + ], + [ + 2, + 6 + ], + [ + 1, + -3 + ], + [ + 5, + -4 + ], + [ + 4, + -2 + ], + [ + 3, + -3 + ], + [ + 2, + 6 + ], + [ + 1, + -2 + ], + [ + 2, + 2 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 42.50637600000027, + 38.003082999999606, + 40.533298000000286, + 37.03745100000015, + 39.681086999999934, + 37.80695100000048, + 37.70319500000005 + ], + "timingMs": { + "min": 37.03745100000015, + "median": 38.003082999999606, + "p95": 42.50637600000027, + "max": 42.50637600000027 + } + }, + "candidate": { + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 2, + 6 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + -5 + ], + [ + 2, + 5 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 1, + -1 + ], + [ + 2, + 5 + ], + [ + 3, + -3 + ], + [ + 4, + -2 + ] + ], + "samplesMs": [ + 0.2412930000000415, + 0.46133300000019517, + 0.2671220000001995, + 0.319922000000588, + 0.23856900000009773, + 0.2317189999994298, + 0.23393300000043382 + ], + "timingMs": { + "min": 0.2317189999994298, + "median": 0.2412930000000415, + "p95": 0.46133300000019517, + "max": 0.46133300000019517 + } + }, + "baseline": { + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 2, + 6 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + -5 + ], + [ + 2, + 5 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 1, + -1 + ], + [ + 2, + 5 + ], + [ + 3, + -3 + ], + [ + 4, + -2 + ] + ], + "samplesMs": [ + 0.2088939999998729, + 0.22677100000009887, + 0.21169800000006944, + 0.2113179999996646, + 0.2134909999995216, + 0.24700099999972736, + 0.2110079999993104 + ], + "timingMs": { + "min": 0.2088939999998729, + "median": 0.21169800000006944, + "p95": 0.24700099999972736, + "max": 0.24700099999972736 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.2661667042743199, + "noiseRatio": 1.378466772403809, + "lowerRatio": 0.9695129497313087, + "upperRatio": 1.6535912423420422 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.2661667042743199, + "noiseRatio": 1.378466772403809, + "lowerRatio": 0.9695129497313087, + "upperRatio": 1.6535912423420422, + "candidate": [ + 0.2412930000000415, + 0.46133300000019517, + 0.2671220000001995, + 0.319922000000588, + 0.23856900000009773, + 0.2317189999994298, + 0.23393300000043382 + ], + "baseline": [ + 0.2088939999998729, + 0.22677100000009887, + 0.21169800000006944, + 0.2113179999996646, + 0.2134909999995216, + 0.24700099999972736, + 0.2110079999993104 + ], + "selfA": [ + 0.16586000000006607, + 0.13501299999961702, + 0.12958500000013373, + 0.1888739999999416, + 0.19473299999981464, + 0.15092699999968318, + 0.2803109999999833 + ], + "selfB": [ + 0.14884399999982634, + 0.18413699999973687, + 0.12364600000000792, + 0.15990000000010696, + 0.14521899999999732, + 0.14154300000018338, + 0.20171400000026551 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.1372870802916664, + "noiseRatio": 1.1749239972387453, + "lowerRatio": 1.0019244619522862, + "upperRatio": 1.2909375428144187, + "candidate": [ + 0.18526899999960733, + 0.1995200000001205, + 0.24083299999983865, + 0.21226000000024214, + 0.1744619999999486, + 0.17715700000007928, + 0.16772299999956886 + ], + "baseline": [ + 0.17606499999965308, + 0.16744199999993725, + 0.17738699999972596, + 0.17598499999985506, + 0.18569900000056805, + 0.1473020000003089, + 0.15811800000028597 + ], + "selfA": [ + 0.1477420000001075, + 0.16531799999938812, + 0.17619500000000698, + 0.15789799999947718, + 0.1470209999997678, + 0.1531699999995908, + 0.1494750000001659 + ], + "selfB": [ + 0.1871820000005755, + 0.1607720000001791, + 0.16396699999950215, + 0.1875820000004751, + 0.13090700000066136, + 0.13126800000009098, + 0.1392889999997351 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.16586000000006607, + 0.13501299999961702, + 0.12958500000013373, + 0.1888739999999416, + 0.19473299999981464, + 0.15092699999968318, + 0.2803109999999833 + ], + "baselineSelfB": [ + 0.14884399999982634, + 0.18413699999973687, + 0.12364600000000792, + 0.15990000000010696, + 0.14521899999999732, + 0.14154300000018338, + 0.20171400000026551 + ] + }, + "historicalReference": { + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 2, + 5 + ], + [ + 3, + -5 + ], + [ + 2, + 6 + ], + [ + 1, + -3 + ], + [ + 5, + -4 + ], + [ + 4, + -2 + ], + [ + 3, + -3 + ], + [ + 2, + 6 + ], + [ + 1, + -2 + ], + [ + 2, + 2 + ], + [ + 0, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "rice-lord-storeroom", + "title": "Rice Lord's storeroom (3rd floor)", + "optimumA": 9, + "solvers": { + "originalBfs": { + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 1 + ], + [ + 4, + 3 + ], + [ + 2, + 4 + ], + [ + 1, + 2 + ], + [ + 0, + 3 + ], + [ + 3, + 5 + ], + [ + 4, + 3 + ], + [ + 1, + 6 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 9.06322099999943, + 8.184038000000328, + 9.087456999999631, + 7.700109999999768, + 9.050752000000102, + 8.856448999999884, + 8.554274000000078 + ], + "timingMs": { + "min": 7.700109999999768, + "median": 8.856448999999884, + "p95": 9.087456999999631, + "max": 9.087456999999631 + } + }, + "upstream": { + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 1 + ], + [ + 4, + 3 + ], + [ + 2, + 4 + ], + [ + 1, + 2 + ], + [ + 0, + 3 + ], + [ + 4, + 3 + ], + [ + 3, + 5 + ], + [ + 1, + 6 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 4.752222000000074, + 5.1414080000004105, + 4.72411000000011, + 4.790299999999661, + 4.828517000000829, + 4.838641999999709, + 4.852383000000373 + ], + "timingMs": { + "min": 4.72411000000011, + "median": 4.828517000000829, + "p95": 5.1414080000004105, + "max": 5.1414080000004105 + } + }, + "candidate": { + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 1 + ], + [ + 4, + 3 + ], + [ + 2, + 4 + ], + [ + 1, + 2 + ], + [ + 0, + 3 + ], + [ + 3, + 5 + ], + [ + 4, + 3 + ], + [ + 1, + 6 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 0.16141300000072079, + 0.13863899999978457, + 0.22659100000055332, + 0.15857799999957933, + 0.13391199999932724, + 0.13361199999962992, + 0.15968100000009144 + ], + "timingMs": { + "min": 0.13361199999962992, + "median": 0.15857799999957933, + "p95": 0.22659100000055332, + "max": 0.22659100000055332 + } + }, + "baseline": { + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 1 + ], + [ + 4, + 3 + ], + [ + 2, + 4 + ], + [ + 1, + 2 + ], + [ + 0, + 3 + ], + [ + 3, + 5 + ], + [ + 4, + 3 + ], + [ + 1, + 6 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 0.12455700000009529, + 0.1268209999998362, + 0.1233250000004773, + 0.11865899999975227, + 0.12407699999948818, + 0.12579900000037014, + 0.11727699999937613 + ], + "timingMs": { + "min": 0.11727699999937613, + "median": 0.12407699999948818, + "p95": 0.1268209999998362, + "max": 0.1268209999998362 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.2733921934165369, + "noiseRatio": 1.4280139558234128, + "lowerRatio": 1.039203060604052, + "upperRatio": 1.5603569116815743 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.2733921934165369, + "noiseRatio": 1.4280139558234128, + "lowerRatio": 1.039203060604052, + "upperRatio": 1.5603569116815743, + "candidate": [ + 0.16141300000072079, + 0.13863899999978457, + 0.22659100000055332, + 0.15857799999957933, + 0.13391199999932724, + 0.13361199999962992, + 0.15968100000009144 + ], + "baseline": [ + 0.12455700000009529, + 0.1268209999998362, + 0.1233250000004773, + 0.11865899999975227, + 0.12407699999948818, + 0.12579900000037014, + 0.11727699999937613 + ], + "selfA": [ + 0.07259999999951106, + 0.08948500000042259, + 0.0693940000001021, + 0.07046600000012404, + 0.10425699999996141, + 0.06783200000063516, + 0.1344330000001719 + ], + "selfB": [ + 0.07020600000032573, + 0.06903400000010151, + 0.07009500000003754, + 0.06841299999996409, + 0.08381600000029721, + 0.07094700000016019, + 0.07982100000026549 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.258119388779651, + "noiseRatio": 1.5119357617125924, + "lowerRatio": 1.0716904853717635, + "upperRatio": 1.4769790513482033, + "candidate": [ + 0.10513799999989715, + 0.10613999999986845, + 0.2125900000000911, + 0.10628999999971711, + 0.10214400000040769, + 0.10185300000011921, + 0.10141299999941111 + ], + "baseline": [ + 0.10283499999968626, + 0.06833300000016607, + 0.15487300000040705, + 0.09463299999970332, + 0.08783200000016222, + 0.06946500000049127, + 0.08485799999925803 + ], + "selfA": [ + 0.08284399999956804, + 0.0675519999995231, + 0.09455199999956676, + 0.08719099999962054, + 0.07419200000003912, + 0.06814200000007986, + 0.12337600000046223 + ], + "selfB": [ + 0.07309999999961292, + 0.08781199999975797, + 0.1564860000007684, + 0.09386099999937869, + 0.08838300000024901, + 0.08859299999949144, + 0.0846769999998287 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.07259999999951106, + 0.08948500000042259, + 0.0693940000001021, + 0.07046600000012404, + 0.10425699999996141, + 0.06783200000063516, + 0.1344330000001719 + ], + "baselineSelfB": [ + 0.07020600000032573, + 0.06903400000010151, + 0.07009500000003754, + 0.06841299999996409, + 0.08381600000029721, + 0.07094700000016019, + 0.07982100000026549 + ] + }, + "historicalReference": { + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 1 + ], + [ + 4, + 3 + ], + [ + 2, + 4 + ], + [ + 1, + 2 + ], + [ + 0, + 3 + ], + [ + 4, + 3 + ], + [ + 3, + 5 + ], + [ + 1, + 6 + ], + [ + 0, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "gomez-chambers-door", + "title": "Door to Gomez's chambers", + "optimumA": 9, + "solvers": { + "originalBfs": { + "A": 9, + "U": 6, + "C": 28, + "plateSwitches": 8, + "commands": [ + [ + 2, + 2 + ], + [ + 1, + -2 + ], + [ + 2, + 2 + ], + [ + 3, + -4 + ], + [ + 0, + -3 + ], + [ + 4, + -4 + ], + [ + 5, + -4 + ], + [ + 3, + -4 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 76.61845400000038, + 77.95652700000028, + 74.67651099999966, + 73.78643199999988, + 76.68536500000027, + 73.76120300000002, + 74.00531200000023 + ], + "timingMs": { + "min": 73.76120300000002, + "median": 74.67651099999966, + "p95": 77.95652700000028, + "max": 77.95652700000028 + } + }, + "upstream": { + "A": 9, + "U": 6, + "C": 28, + "plateSwitches": 8, + "commands": [ + [ + 2, + 2 + ], + [ + 1, + -2 + ], + [ + 4, + -4 + ], + [ + 3, + -5 + ], + [ + 1, + -3 + ], + [ + 3, + -3 + ], + [ + 5, + -4 + ], + [ + 2, + 2 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 31.29958700000043, + 32.98707699999977, + 31.310483000000204, + 30.83755199999996, + 30.732804000000215, + 31.10607600000003, + 29.940330999999787 + ], + "timingMs": { + "min": 29.940330999999787, + "median": 31.10607600000003, + "p95": 32.98707699999977, + "max": 32.98707699999977 + } + }, + "candidate": { + "A": 9, + "U": 6, + "C": 28, + "plateSwitches": 8, + "commands": [ + [ + 3, + -3 + ], + [ + 2, + 2 + ], + [ + 1, + -2 + ], + [ + 0, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -4 + ], + [ + 5, + -4 + ], + [ + 3, + -5 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 0.18520899999930407, + 0.18272499999966385, + 0.20560999999997875, + 0.20275500000025204, + 0.2023140000001149, + 0.24476899999990565, + 0.19783800000004703 + ], + "timingMs": { + "min": 0.18272499999966385, + "median": 0.2023140000001149, + "p95": 0.24476899999990565, + "max": 0.24476899999990565 + } + }, + "baseline": { + "A": 9, + "U": 6, + "C": 28, + "plateSwitches": 8, + "commands": [ + [ + 3, + -3 + ], + [ + 2, + 2 + ], + [ + 1, + -2 + ], + [ + 0, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -4 + ], + [ + 5, + -4 + ], + [ + 3, + -5 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 0.1711780000005092, + 0.23811900000055175, + 0.17112699999961478, + 0.18855399999938527, + 0.1704070000005231, + 0.19064700000035373, + 0.17104699999981676 + ], + "timingMs": { + "min": 0.1704070000005231, + "median": 0.1711780000005092, + "p95": 0.23811900000055175, + "max": 0.23811900000055175 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", + "ratio": 1.0953025797326172, + "noiseRatio": 1.997056279475185, + "lowerRatio": 0.917806297282419, + "upperRatio": 1.307125201386333 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0953025797326172, + "noiseRatio": 1.997056279475185, + "lowerRatio": 0.917806297282419, + "upperRatio": 1.307125201386333, + "candidate": [ + 0.18520899999930407, + 0.18272499999966385, + 0.20560999999997875, + 0.20275500000025204, + 0.2023140000001149, + 0.24476899999990565, + 0.19783800000004703 + ], + "baseline": [ + 0.1711780000005092, + 0.23811900000055175, + 0.17112699999961478, + 0.18855399999938527, + 0.1704070000005231, + 0.19064700000035373, + 0.17104699999981676 + ], + "selfA": [ + 0.1456289999996443, + 0.12461799999982759, + 0.12211399999978312, + 0.12366600000041217, + 0.10585900000023685, + 0.11785800000052404, + 0.10328500000014174 + ], + "selfB": [ + 0.13166799999999057, + 0.1768259999998918, + 0.4124409999994896, + 0.13407100000040373, + 0.09819800000059331, + 0.09606499999972584, + 0.09885900000062975 + ] + }, + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 1.0048653508529528, + "noiseRatio": 1.2318530904862122, + "lowerRatio": 0.8325425916045742, + "upperRatio": 1.2128561151432629, + "candidate": [ + 0.14243400000032125, + 0.14483800000016345, + 0.14515799999935552, + 0.12391699999989214, + 0.11494299999958457, + 0.12594000000080996, + 0.11688599999979488 + ], + "baseline": [ + 0.15070599999944534, + 0.19813799999974435, + 0.13444200000049022, + 0.10912499999994907, + 0.11756700000023557, + 0.09708599999976286, + 0.12137299999994866 + ], + "selfA": [ + 0.13620499999979074, + 0.11972100000002683, + 0.13311999999950785, + 0.12624999999934516, + 0.0984889999999723, + 0.1117180000001099, + 0.13539399999990565 + ], + "selfB": [ + 0.15406099999927392, + 0.15721699999994598, + 0.16378600000007282, + 0.11284999999952561, + 0.0972170000004553, + 0.10681100000056176, + 0.12967500000013388 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.1456289999996443, + 0.12461799999982759, + 0.12211399999978312, + 0.12366600000041217, + 0.10585900000023685, + 0.11785800000052404, + 0.10328500000014174 + ], + "baselineSelfB": [ + 0.13166799999999057, + 0.1768259999998918, + 0.4124409999994896, + 0.13407100000040373, + 0.09819800000059331, + 0.09606499999972584, + 0.09885900000062975 + ] + }, + "historicalReference": { + "A": 9, + "U": 6, + "C": 28, + "plateSwitches": 8, + "commands": [ + [ + 2, + 2 + ], + [ + 1, + -2 + ], + [ + 4, + -4 + ], + [ + 3, + -5 + ], + [ + 1, + -3 + ], + [ + 3, + -3 + ], + [ + 5, + -4 + ], + [ + 2, + 2 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "gomez-throne-hall-chest", + "title": "Chest in Gomez's throne hall", + "optimumA": 5, + "solvers": { + "originalBfs": { + "A": 5, + "U": 4, + "C": 16, + "plateSwitches": 4, + "commands": [ + [ + 3, + 6 + ], + [ + 0, + 6 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 3, + 1 + ] + ], + "samplesMs": [ + 0.18502799999987474, + 0.17443199999979697, + 0.17644500000005792, + 0.17631599999913306, + 0.1844169999994847, + 0.22013200000037614, + 0.17918999999983498 + ], + "timingMs": { + "min": 0.17443199999979697, + "median": 0.17918999999983498, + "p95": 0.22013200000037614, + "max": 0.22013200000037614 + } + }, + "upstream": { + "A": 5, + "U": 4, + "C": 16, + "plateSwitches": 4, + "commands": [ + [ + 3, + 6 + ], + [ + 0, + 6 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 2 + ] + ], + "samplesMs": [ + 0.3334820000000036, + 0.2743529999997918, + 0.2259810000005018, + 0.2515979999998308, + 0.23905999999988126, + 0.23649500000010448, + 0.22482899999977235 + ], + "timingMs": { + "min": 0.22482899999977235, + "median": 0.23905999999988126, + "p95": 0.3334820000000036, + "max": 0.3334820000000036 + } + }, + "candidate": { + "A": 5, + "U": 4, + "C": 16, + "plateSwitches": 4, + "commands": [ + [ + 3, + 6 + ], + [ + 0, + 6 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 3, + 1 + ] + ], + "samplesMs": [ + 0.02808199999981298, + 0.02301499999975931, + 0.024336999999832187, + 0.029954999999972642, + 0.05179800000041723, + 0.02257400000053167, + 0.02509699999973236 + ], + "timingMs": { + "min": 0.02257400000053167, + "median": 0.02509699999973236, + "p95": 0.05179800000041723, + "max": 0.05179800000041723 + } + }, + "baseline": { + "A": 5, + "U": 4, + "C": 16, + "plateSwitches": 4, + "commands": [ + [ + 3, + 6 + ], + [ + 0, + 6 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 3, + 1 + ] + ], + "samplesMs": [ + 0.030535999999301566, + 0.024425999999948544, + 0.025058000000171887, + 0.02899299999990035, + 0.023505000000113796, + 0.022174000000632077, + 0.025397999999768217 + ], + "timingMs": { + "min": 0.022174000000632077, + "median": 0.025058000000171887, + "p95": 0.030535999999301566, + "max": 0.030535999999301566 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.098286972484325, + "noiseRatio": 1.1688702586130604, + "lowerRatio": 0.7935295721410459, + "upperRatio": 1.52008736192932 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.098286972484325, + "noiseRatio": 1.1688702586130604, + "lowerRatio": 0.7935295721410459, + "upperRatio": 1.52008736192932, + "candidate": [ + 0.02808199999981298, + 0.02301499999975931, + 0.024336999999832187, + 0.029954999999972642, + 0.05179800000041723, + 0.02257400000053167, + 0.02509699999973236 + ], + "baseline": [ + 0.030535999999301566, + 0.024425999999948544, + 0.025058000000171887, + 0.02899299999990035, + 0.023505000000113796, + 0.022174000000632077, + 0.025397999999768217 + ], + "selfA": [ + 0.015903999999864027, + 0.01553299999977753, + 0.015212000000246917, + 0.015603000000737666, + 0.015753000000586326, + 0.015693000000283064, + 0.01557299999967654 + ], + "selfB": [ + 0.01758700000027602, + 0.015913999999611406, + 0.015473999999812804, + 0.01727599999958329, + 0.01535299999977724, + 0.017126000000644126, + 0.019268999999439984 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.8984484274970228, + "noiseRatio": 1.3836720164698226, + "lowerRatio": 0.555209174368421, + "upperRatio": 1.4538837147100736, + "candidate": [ + 0.016765000000305008, + 0.01981900000009773, + 0.01693599999998696, + 0.01614400000016758, + 0.023946000000250933, + 0.02127199999995355, + 0.018207000000074913 + ], + "baseline": [ + 0.015754000000015367, + 0.04057100000045466, + 0.017717000000629923, + 0.03558399999928952, + 0.019710000000486616, + 0.014962999999625026, + 0.015002999999524036 + ], + "selfA": [ + 0.017295999999987544, + 0.016775999999481428, + 0.018216999999822292, + 0.029314000000340457, + 0.016695000000254367, + 0.014983000000029278, + 0.01515300000028219 + ], + "selfB": [ + 0.01573400000052061, + 0.01626399999986461, + 0.015903999999864027, + 0.01723599999968428, + 0.015223000000332831, + 0.01785699999982171, + 0.017406999999366235 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.015903999999864027, + 0.01553299999977753, + 0.015212000000246917, + 0.015603000000737666, + 0.015753000000586326, + 0.015693000000283064, + 0.01557299999967654 + ], + "baselineSelfB": [ + 0.01758700000027602, + 0.015913999999611406, + 0.015473999999812804, + 0.01727599999958329, + 0.01535299999977724, + 0.017126000000644126, + 0.019268999999439984 + ] + }, + "historicalReference": { + "A": 5, + "U": 4, + "C": 16, + "plateSwitches": 4, + "commands": [ + [ + 3, + 6 + ], + [ + 0, + 6 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 2 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "old-camp-dungeon-door", + "title": "Dungeon door by the executed ghost", + "optimumA": 17, + "solvers": { + "originalBfs": { + "A": 17, + "U": 6, + "C": 72, + "plateSwitches": 16, + "commands": [ + [ + 3, + 2 + ], + [ + 4, + -2 + ], + [ + 1, + -1 + ], + [ + 2, + -4 + ], + [ + 0, + -5 + ], + [ + 5, + -6 + ], + [ + 4, + -5 + ], + [ + 5, + -5 + ], + [ + 2, + -6 + ], + [ + 0, + -6 + ], + [ + 1, + -6 + ], + [ + 0, + -6 + ], + [ + 3, + -3 + ], + [ + 5, + -6 + ], + [ + 2, + -3 + ], + [ + 4, + -3 + ], + [ + 5, + -3 + ] + ], + "samplesMs": [ + 90.52278000000024, + 87.66105399999924, + 90.33665099999962, + 92.7997919999998, + 91.53709700000036, + 125.40649699999994, + 90.29265399999986 + ], + "timingMs": { + "min": 87.66105399999924, + "median": 90.52278000000024, + "p95": 125.40649699999994, + "max": 125.40649699999994 + } + }, + "upstream": { + "A": 18, + "U": 6, + "C": 68, + "plateSwitches": 17, + "commands": [ + [ + 4, + -3 + ], + [ + 2, + -3 + ], + [ + 5, + -6 + ], + [ + 0, + -3 + ], + [ + 1, + -3 + ], + [ + 2, + -3 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 4, + -6 + ], + [ + 5, + -6 + ], + [ + 2, + -5 + ], + [ + 5, + -5 + ], + [ + 0, + -5 + ], + [ + 1, + -4 + ], + [ + 4, + -1 + ], + [ + 3, + -1 + ], + [ + 2, + -2 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 49.4057929999999, + 44.25354399999924, + 42.91580999999951, + 41.27184600000055, + 41.655573000000004, + 42.125340000000506, + 42.866986999999426 + ], + "timingMs": { + "min": 41.27184600000055, + "median": 42.866986999999426, + "p95": 49.4057929999999, + "max": 49.4057929999999 + } + }, + "candidate": { + "A": 17, + "U": 6, + "C": 72, + "plateSwitches": 16, + "commands": [ + [ + 4, + -2 + ], + [ + 1, + -1 + ], + [ + 3, + 2 + ], + [ + 2, + -4 + ], + [ + 5, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + -5 + ], + [ + 5, + -6 + ], + [ + 2, + -6 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 1, + -6 + ], + [ + 0, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ], + [ + 5, + -6 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 2.243797000000086, + 2.1073319999995874, + 2.8227689999994254, + 2.0736109999998007, + 2.4010239999997793, + 2.1424639999995634, + 2.2628960000001825 + ], + "timingMs": { + "min": 2.0736109999998007, + "median": 2.243797000000086, + "p95": 2.8227689999994254, + "max": 2.8227689999994254 + } + }, + "baseline": { + "A": 17, + "U": 6, + "C": 72, + "plateSwitches": 16, + "commands": [ + [ + 4, + -2 + ], + [ + 1, + -1 + ], + [ + 3, + 2 + ], + [ + 2, + -4 + ], + [ + 5, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + -5 + ], + [ + 5, + -6 + ], + [ + 2, + -6 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 1, + -6 + ], + [ + 0, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ], + [ + 5, + -6 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 2.316385999999511, + 2.3156350000008388, + 2.279461000000083, + 2.2497960000000603, + 2.2515990000001693, + 2.1491150000001653, + 2.3162560000000667 + ], + "timingMs": { + "min": 2.1491150000001653, + "median": 2.279461000000083, + "p95": 2.316385999999511, + "max": 2.316385999999511 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", + "ratio": 1.006302605015658, + "noiseRatio": 1.0354971548980059, + "lowerRatio": 0.9009490475724129, + "upperRatio": 1.1239758070557362 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.006302605015658, + "noiseRatio": 1.0354971548980059, + "lowerRatio": 0.9009490475724129, + "upperRatio": 1.1239758070557362, + "candidate": [ + 2.243797000000086, + 2.1073319999995874, + 2.8227689999994254, + 2.0736109999998007, + 2.4010239999997793, + 2.1424639999995634, + 2.2628960000001825 + ], + "baseline": [ + 2.316385999999511, + 2.3156350000008388, + 2.279461000000083, + 2.2497960000000603, + 2.2515990000001693, + 2.1491150000001653, + 2.3162560000000667 + ], + "selfA": [ + 2.163226000000577, + 2.1978369999997085, + 2.169014000000061, + 2.1042370000004667, + 2.130677000000105, + 2.083144999999604, + 2.153741999999511 + ], + "selfB": [ + 2.199239999999918, + 2.1076819999998406, + 2.173370999999861, + 2.224457999999686, + 2.088564000000588, + 2.1182980000003226, + 2.0986880000000383 + ] + }, + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.9176887142504068, + "noiseRatio": 1.0929010914639148, + "lowerRatio": 0.8009103285317423, + "upperRatio": 1.0514942138484207, + "candidate": [ + 2.4204629999994722, + 2.274282999999741, + 2.3416550000001735, + 2.299149999999827, + 2.084907999999814, + 2.043265000000247, + 2.107111000000259 + ], + "baseline": [ + 2.6924419999995735, + 2.2727209999993647, + 2.285029000000577, + 3.306525999999394, + 2.162405000000035, + 2.1541919999999664, + 2.2536520000003293 + ], + "selfA": [ + 2.6162569999996776, + 2.4628169999996317, + 2.1627150000003894, + 2.1810230000000956, + 2.1425349999999526, + 2.0998510000008537, + 2.1898160000000644 + ], + "selfB": [ + 2.4278629999998884, + 2.4364980000000287, + 2.179070000000138, + 2.544639000000643, + 2.1554740000001402, + 2.168913999999859, + 2.1863809999995283 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 2.163226000000577, + 2.1978369999997085, + 2.169014000000061, + 2.1042370000004667, + 2.130677000000105, + 2.083144999999604, + 2.153741999999511 + ], + "baselineSelfB": [ + 2.199239999999918, + 2.1076819999998406, + 2.173370999999861, + 2.224457999999686, + 2.088564000000588, + 2.1182980000003226, + 2.0986880000000383 + ] + }, + "historicalReference": { + "A": 18, + "U": 6, + "C": 68, + "plateSwitches": 17, + "commands": [ + [ + 4, + -3 + ], + [ + 2, + -3 + ], + [ + 5, + -6 + ], + [ + 0, + -3 + ], + [ + 1, + -3 + ], + [ + 2, + -3 + ], + [ + 5, + -3 + ], + [ + 0, + -6 + ], + [ + 4, + -6 + ], + [ + 5, + -6 + ], + [ + 2, + -5 + ], + [ + 5, + -5 + ], + [ + 0, + -5 + ], + [ + 1, + -4 + ], + [ + 4, + -1 + ], + [ + 3, + -1 + ], + [ + 2, + -2 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "regression", + "deltas": { + "A": -1, + "U": 0, + "C": 4, + "plateSwitches": -1 + }, + "regressions": [ + "C" + ] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "arlin-dungeon-key", + "title": "Arlin's dungeon key", + "optimumA": 7, + "solvers": { + "originalBfs": { + "A": 7, + "U": 5, + "C": 25, + "plateSwitches": 6, + "commands": [ + [ + 2, + -2 + ], + [ + 5, + -2 + ], + [ + 6, + -6 + ], + [ + 0, + -3 + ], + [ + 2, + -6 + ], + [ + 1, + 3 + ], + [ + 5, + -3 + ] + ], + "samplesMs": [ + 49.07597900000019, + 48.175131999999394, + 50.91433400000005, + 48.47274000000016, + 47.77182500000072, + 46.63624599999912, + 49.09639799999968 + ], + "timingMs": { + "min": 46.63624599999912, + "median": 48.47274000000016, + "p95": 50.91433400000005, + "max": 50.91433400000005 + } + }, + "upstream": { + "A": 7, + "U": 5, + "C": 25, + "plateSwitches": 6, + "commands": [ + [ + 2, + -2 + ], + [ + 6, + -6 + ], + [ + 2, + -3 + ], + [ + 5, + -5 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 21.109174000000166, + 21.5032369999999, + 21.403597000000445, + 21.04023100000086, + 21.470196999999644, + 21.44747400000051, + 20.977767000000313 + ], + "timingMs": { + "min": 20.977767000000313, + "median": 21.403597000000445, + "p95": 21.5032369999999, + "max": 21.5032369999999 + } + }, + "candidate": { + "A": 7, + "U": 5, + "C": 25, + "plateSwitches": 6, + "commands": [ + [ + 2, + -2 + ], + [ + 5, + -2 + ], + [ + 6, + -6 + ], + [ + 0, + -3 + ], + [ + 2, + -6 + ], + [ + 1, + 3 + ], + [ + 5, + -3 + ] + ], + "samplesMs": [ + 7.786336000000119, + 7.5857050000004165, + 7.949791999999434, + 11.08267700000033, + 8.164826000000176, + 8.045687000001635, + 7.698093000000881 + ], + "timingMs": { + "min": 7.5857050000004165, + "median": 7.949791999999434, + "p95": 11.08267700000033, + "max": 11.08267700000033 + } + }, + "baseline": { + "A": 7, + "U": 5, + "C": 25, + "plateSwitches": 6, + "commands": [ + [ + 2, + -2 + ], + [ + 5, + -2 + ], + [ + 6, + -6 + ], + [ + 0, + -3 + ], + [ + 2, + -6 + ], + [ + 1, + 3 + ], + [ + 5, + -3 + ] + ], + "samplesMs": [ + 8.873804999999265, + 7.581557999999859, + 9.488448000000062, + 11.834627999999611, + 7.734927999999854, + 7.649639999999636, + 8.985781000001225 + ], + "timingMs": { + "min": 7.581557999999859, + "median": 8.873804999999265, + "p95": 11.834627999999611, + "max": 11.834627999999611 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.9413789915267663, + "noiseRatio": 1.3054522689394972, + "lowerRatio": 0.8502056094171313, + "upperRatio": 1.042329521085485 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9413789915267663, + "noiseRatio": 1.3054522689394972, + "lowerRatio": 0.8502056094171313, + "upperRatio": 1.042329521085485, + "candidate": [ + 7.786336000000119, + 7.5857050000004165, + 7.949791999999434, + 11.08267700000033, + 8.164826000000176, + 8.045687000001635, + 7.698093000000881 + ], + "baseline": [ + 8.873804999999265, + 7.581557999999859, + 9.488448000000062, + 11.834627999999611, + 7.734927999999854, + 7.649639999999636, + 8.985781000001225 + ], + "selfA": [ + 9.020376000000397, + 8.74142700000084, + 8.722897999999986, + 14.555786999999327, + 9.61799500000052, + 8.00543899999957, + 8.643188999999438 + ], + "selfB": [ + 8.655327000000398, + 7.866339000000153, + 8.522515999999996, + 9.86421700000028, + 8.223336999999447, + 9.90675100000044, + 8.28538000000026 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.02890269964583, + "noiseRatio": 1.2233811416595932, + "lowerRatio": 0.7594313844191403, + "upperRatio": 1.3939913296422302, + "candidate": [ + 9.4491280000002, + 10.120609000001423, + 8.276654000001145, + 10.115431000000171, + 7.68337099999917, + 10.484907000000021, + 7.981069999999818 + ], + "baseline": [ + 12.687918000001446, + 7.484291999999186, + 12.554842000001372, + 7.611973999999464, + 7.567999000000782, + 7.9831429999994725, + 7.691133000000264 + ], + "selfA": [ + 7.760707999999795, + 7.557883000001311, + 7.572875000001659, + 7.514137000000119, + 7.608208999999988, + 11.387746000000334, + 7.910373000000618 + ], + "selfB": [ + 7.76864999999998, + 7.878064999998969, + 7.5173610000001645, + 7.664923999998791, + 7.647297000001345, + 7.85381899999993, + 7.573656999999002 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 9.020376000000397, + 8.74142700000084, + 8.722897999999986, + 14.555786999999327, + 9.61799500000052, + 8.00543899999957, + 8.643188999999438 + ], + "baselineSelfB": [ + 8.655327000000398, + 7.866339000000153, + 8.522515999999996, + 9.86421700000028, + 8.223336999999447, + 9.90675100000044, + 8.28538000000026 + ] + }, + "historicalReference": { + "A": 7, + "U": 5, + "C": 25, + "plateSwitches": 6, + "commands": [ + [ + 2, + -2 + ], + [ + 6, + -6 + ], + [ + 2, + -3 + ], + [ + 5, + -5 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "gomez-wine-cellar", + "title": "Gomez's wine cellar", + "optimumA": 9, + "solvers": { + "originalBfs": { + "A": 9, + "U": 6, + "C": 41, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 2, + 5 + ], + [ + 5, + 4 + ], + [ + 3, + 4 + ], + [ + 2, + -6 + ], + [ + 4, + -5 + ], + [ + 3, + 6 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 59.59877199999937, + 59.65067900000031, + 57.79490699999951, + 56.794411999999284, + 57.24158499999976, + 60.53570200000104, + 73.16609699999935 + ], + "timingMs": { + "min": 56.794411999999284, + "median": 59.59877199999937, + "p95": 73.16609699999935, + "max": 73.16609699999935 + } + }, + "upstream": { + "A": 11, + "U": 6, + "C": 31, + "plateSwitches": 10, + "commands": [ + [ + 3, + 1 + ], + [ + 4, + -2 + ], + [ + 3, + 2 + ], + [ + 5, + 4 + ], + [ + 3, + 4 + ], + [ + 4, + -6 + ], + [ + 2, + -1 + ], + [ + 3, + 3 + ], + [ + 4, + -3 + ], + [ + 1, + -2 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 27.881734999999026, + 27.45158899999842, + 28.345423000000665, + 27.98457000000053, + 30.36988999999994, + 28.340355000000272, + 29.158136000000013 + ], + "timingMs": { + "min": 27.45158899999842, + "median": 28.340355000000272, + "p95": 30.36988999999994, + "max": 30.36988999999994 + } + }, + "candidate": { + "A": 9, + "U": 6, + "C": 41, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 2, + 5 + ], + [ + 5, + 4 + ], + [ + 3, + 4 + ], + [ + 2, + -6 + ], + [ + 4, + -5 + ], + [ + 3, + 6 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 0.1863999999986845, + 0.1045670000003156, + 0.11899900000025809, + 0.11747699999978067, + 0.11929000000054657, + 0.1116280000005645, + 0.22822400000040943 + ], + "timingMs": { + "min": 0.1045670000003156, + "median": 0.11899900000025809, + "p95": 0.22822400000040943, + "max": 0.22822400000040943 + } + }, + "baseline": { + "A": 9, + "U": 6, + "C": 41, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 2, + 5 + ], + [ + 5, + 4 + ], + [ + 3, + 4 + ], + [ + 2, + -6 + ], + [ + 4, + -5 + ], + [ + 3, + 6 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 0.09228900000016438, + 0.08511799999905634, + 0.08879399999932502, + 0.10961499999939406, + 0.1684640000003128, + 0.08565900000030524, + 0.09263000000100874 + ], + "timingMs": { + "min": 0.08511799999905634, + "median": 0.09228900000016438, + "p95": 0.1684640000003128, + "max": 0.1684640000003128 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.3483500011099405, + "noiseRatio": 1.6646882648812724, + "lowerRatio": 0.8777996584971208, + "upperRatio": 2.0711419831329763 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.3483500011099405, + "noiseRatio": 1.6646882648812724, + "lowerRatio": 0.8777996584971208, + "upperRatio": 2.0711419831329763, + "candidate": [ + 0.1863999999986845, + 0.1045670000003156, + 0.11899900000025809, + 0.11747699999978067, + 0.11929000000054657, + 0.1116280000005645, + 0.22822400000040943 + ], + "baseline": [ + 0.09228900000016438, + 0.08511799999905634, + 0.08879399999932502, + 0.10961499999939406, + 0.1684640000003128, + 0.08565900000030524, + 0.09263000000100874 + ], + "selfA": [ + 0.12335500000153843, + 0.11847799999850395, + 0.11239899999964109, + 0.1932010000000446, + 0.13438200000018696, + 0.054151999998794054, + 0.05409200000030978 + ], + "selfB": [ + 0.14424700000017765, + 0.12425599999914994, + 0.1141219999990426, + 0.11610400000063237, + 0.06396700000004785, + 0.055883999999423395, + 0.048773000000437605 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.8996419177963317, + "noiseRatio": 2.033179562713795, + "lowerRatio": 1.091985258245267, + "upperRatio": 3.304659461839001, + "candidate": [ + 0.07019500000023982, + 0.06825199999912002, + 0.07229900000129419, + 0.3428249999997206, + 0.1518579999992653, + 0.13267000000087137, + 0.06638000000020838 + ], + "baseline": [ + 0.061402999999700114, + 0.047902000000249245, + 0.049924999999348074, + 0.06497800000033749, + 0.06417700000019977, + 0.05796799999916402, + 0.050116000000343774 + ], + "selfA": [ + 0.05384100000082981, + 0.044447000000218395, + 0.04970499999944877, + 0.05606499999885273, + 0.04966499999864027, + 0.05872799999997369, + 0.06912499999998545 + ], + "selfB": [ + 0.05457300000125542, + 0.043426000000181375, + 0.0860290000000532, + 0.0498250000000553, + 0.14181300000018382, + 0.08713200000056531, + 0.058007999999972526 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.12335500000153843, + 0.11847799999850395, + 0.11239899999964109, + 0.1932010000000446, + 0.13438200000018696, + 0.054151999998794054, + 0.05409200000030978 + ], + "baselineSelfB": [ + 0.14424700000017765, + 0.12425599999914994, + 0.1141219999990426, + 0.11610400000063237, + 0.06396700000004785, + 0.055883999999423395, + 0.048773000000437605 + ] + }, + "historicalReference": { + "A": 11, + "U": 6, + "C": 31, + "plateSwitches": 10, + "commands": [ + [ + 3, + 1 + ], + [ + 4, + -2 + ], + [ + 3, + 2 + ], + [ + 5, + 4 + ], + [ + 3, + 4 + ], + [ + 4, + -6 + ], + [ + 2, + -1 + ], + [ + 3, + 3 + ], + [ + 4, + -3 + ], + [ + 1, + -2 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "regression", + "deltas": { + "A": -2, + "U": 0, + "C": 10, + "plateSwitches": -2 + }, + "regressions": [ + "C" + ] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "gomez-bedside-chest", + "title": "Gomez bedside chest", + "optimumA": 23, + "solvers": { + "originalBfs": { + "A": 23, + "U": 5, + "C": 68, + "plateSwitches": 22, + "commands": [ + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 1, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 4, + 1 + ], + [ + 2, + -1 + ], + [ + 0, + -2 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ], + [ + 1, + -3 + ], + [ + 4, + 3 + ], + [ + 2, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + -6 + ], + [ + 1, + -6 + ], + [ + 3, + -5 + ], + [ + 4, + 3 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + 3 + ], + [ + 1, + -6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 10.746721000001344, + 10.780200999999579, + 10.484566999999515, + 11.635549000000537, + 11.074744999999893, + 10.778028000000631, + 10.780791999999565 + ], + "timingMs": { + "min": 10.484566999999515, + "median": 10.780200999999579, + "p95": 11.635549000000537, + "max": 11.635549000000537 + } + }, + "upstream": { + "A": 23, + "U": 5, + "C": 68, + "plateSwitches": 22, + "commands": [ + [ + 2, + -1 + ], + [ + 1, + -1 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -4 + ], + [ + 4, + 2 + ], + [ + 0, + -2 + ], + [ + 2, + -2 + ], + [ + 1, + -4 + ], + [ + 0, + -6 + ], + [ + 1, + -6 + ], + [ + 4, + 3 + ], + [ + 3, + -5 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + 3 + ], + [ + 1, + -6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 10.757768000001306, + 8.769925999999032, + 8.463854999999967, + 8.778299999999945, + 9.44001500000013, + 8.635173000000577, + 8.045016000000032 + ], + "timingMs": { + "min": 8.045016000000032, + "median": 8.769925999999032, + "p95": 10.757768000001306, + "max": 10.757768000001306 + } + }, + "candidate": { + "A": 23, + "U": 5, + "C": 68, + "plateSwitches": 22, + "commands": [ + [ + 2, + -1 + ], + [ + 1, + -1 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -4 + ], + [ + 0, + -4 + ], + [ + 1, + -4 + ], + [ + 4, + 2 + ], + [ + 2, + -2 + ], + [ + 3, + -5 + ], + [ + 0, + -4 + ], + [ + 1, + -6 + ], + [ + 4, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + 3 + ], + [ + 1, + -6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 1.8123070000001462, + 1.4463869999999588, + 1.5601980000010371, + 2.550982999999178, + 1.6436040000007779, + 1.4755199999999604, + 1.7677700000003824 + ], + "timingMs": { + "min": 1.4463869999999588, + "median": 1.6436040000007779, + "p95": 2.550982999999178, + "max": 2.550982999999178 + } + }, + "baseline": { + "A": 23, + "U": 5, + "C": 68, + "plateSwitches": 22, + "commands": [ + [ + 2, + -1 + ], + [ + 1, + -1 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -4 + ], + [ + 0, + -4 + ], + [ + 1, + -4 + ], + [ + 4, + 2 + ], + [ + 2, + -2 + ], + [ + 3, + -5 + ], + [ + 0, + -4 + ], + [ + 1, + -6 + ], + [ + 4, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + 3 + ], + [ + 1, + -6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 1.5166619999999966, + 1.5091409999986354, + 1.6094819999998435, + 2.3969969999998284, + 1.5684000000001106, + 1.8698530000001483, + 1.557993999998871 + ], + "timingMs": { + "min": 1.5091409999986354, + "median": 1.5684000000001106, + "p95": 2.3969969999998284, + "max": 2.3969969999998284 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", + "ratio": 1.014835411222671, + "noiseRatio": 1.4941160366628865, + "lowerRatio": 0.8798147108014918, + "upperRatio": 1.1705770535858397 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.014835411222671, + "noiseRatio": 1.4941160366628865, + "lowerRatio": 0.8798147108014918, + "upperRatio": 1.1705770535858397, + "candidate": [ + 1.8123070000001462, + 1.4463869999999588, + 1.5601980000010371, + 2.550982999999178, + 1.6436040000007779, + 1.4755199999999604, + 1.7677700000003824 + ], + "baseline": [ + 1.5166619999999966, + 1.5091409999986354, + 1.6094819999998435, + 2.3969969999998284, + 1.5684000000001106, + 1.8698530000001483, + 1.557993999998871 + ], + "selfA": [ + 1.7827120000001742, + 1.4631010000011884, + 1.7880009999989852, + 1.5858869999992748, + 1.545405999999275, + 1.559917000000496, + 3.7770029999992403 + ], + "selfB": [ + 1.491173999998864, + 1.5276580000008835, + 1.571364999999787, + 1.5719850000004953, + 1.533116999999038, + 2.1329100000002654, + 1.949663999999757 + ] + }, + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 1.0168485077623741, + "noiseRatio": 1.2009298393321017, + "lowerRatio": 0.8635720868931483, + "upperRatio": 1.1973301400448153, + "candidate": [ + 2.2693949999993492, + 1.72632799999883, + 1.4953810000006342, + 1.441408999999112, + 1.447018000000753, + 1.622201000000132, + 1.5337479999998322 + ], + "baseline": [ + 1.611895000000004, + 1.812448000000586, + 1.4912939999994705, + 1.6364419999990787, + 1.5943889999998646, + 1.5431419999986247, + 1.5418599999993603 + ], + "selfA": [ + 1.4916639999992185, + 1.953589999999167, + 1.509001000000353, + 2.0842869999996765, + 1.947900999999547, + 1.8309650000010151, + 1.5705930000003718 + ], + "selfB": [ + 1.6326559999997698, + 1.6040740000007645, + 1.4744590000009339, + 1.6683310000007623, + 1.852548000000752, + 1.7716760000002978, + 1.4839430000010907 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 1.7827120000001742, + 1.4631010000011884, + 1.7880009999989852, + 1.5858869999992748, + 1.545405999999275, + 1.559917000000496, + 3.7770029999992403 + ], + "baselineSelfB": [ + 1.491173999998864, + 1.5276580000008835, + 1.571364999999787, + 1.5719850000004953, + 1.533116999999038, + 2.1329100000002654, + 1.949663999999757 + ] + }, + "historicalReference": { + "A": 23, + "U": 5, + "C": 68, + "plateSwitches": 22, + "commands": [ + [ + 2, + -1 + ], + [ + 1, + -1 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + -4 + ], + [ + 4, + 2 + ], + [ + 0, + -2 + ], + [ + 2, + -2 + ], + [ + 1, + -4 + ], + [ + 0, + -6 + ], + [ + 1, + -6 + ], + [ + 4, + 3 + ], + [ + 3, + -5 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + 3 + ], + [ + 1, + -6 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "gomez-wall-chest", + "title": "Wall chest in Gomez's chambers", + "optimumA": 20, + "solvers": { + "originalBfs": { + "A": 20, + "U": 6, + "C": 75, + "plateSwitches": 19, + "commands": [ + [ + 0, + -2 + ], + [ + 3, + -2 + ], + [ + 4, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 5, + -6 + ], + [ + 0, + -5 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 3, + -4 + ], + [ + 5, + -5 + ], + [ + 0, + -2 + ], + [ + 2, + 4 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ], + [ + 1, + -3 + ], + [ + 5, + -2 + ] + ], + "samplesMs": [ + 94.44142199999987, + 95.14554099999987, + 103.42940699999963, + 108.77189499999986, + 102.2110130000001, + 101.36178499999914, + 95.80923000000075 + ], + "timingMs": { + "min": 94.44142199999987, + "median": 101.36178499999914, + "p95": 108.77189499999986, + "max": 108.77189499999986 + } + }, + "upstream": { + "A": 20, + "U": 6, + "C": 75, + "plateSwitches": 19, + "commands": [ + [ + 0, + -2 + ], + [ + 4, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ], + [ + 5, + -6 + ], + [ + 3, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 0, + -5 + ], + [ + 5, + -5 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 3, + -3 + ], + [ + 0, + -2 + ], + [ + 5, + -2 + ], + [ + 2, + 4 + ], + [ + 4, + -6 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 88.82958700000017, + 87.62570099999903, + 104.45869599999969, + 80.36745399999927, + 83.08285000000069, + 84.6270940000013, + 89.74200000000019 + ], + "timingMs": { + "min": 80.36745399999927, + "median": 87.62570099999903, + "p95": 104.45869599999969, + "max": 104.45869599999969 + } + }, + "candidate": { + "A": 20, + "U": 6, + "C": 75, + "plateSwitches": 19, + "commands": [ + [ + 0, + -2 + ], + [ + 4, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ], + [ + 5, + -6 + ], + [ + 3, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 3, + -3 + ], + [ + 5, + -5 + ], + [ + 0, + -2 + ], + [ + 5, + -2 + ], + [ + 2, + 4 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 5.743753000000652, + 7.4569809999993595, + 5.275819000000411, + 6.521903999999267, + 6.599210000000312, + 5.40407100000084, + 5.326575999999477 + ], + "timingMs": { + "min": 5.275819000000411, + "median": 5.743753000000652, + "p95": 7.4569809999993595, + "max": 7.4569809999993595 + } + }, + "baseline": { + "A": 20, + "U": 6, + "C": 75, + "plateSwitches": 19, + "commands": [ + [ + 0, + -2 + ], + [ + 4, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ], + [ + 5, + -6 + ], + [ + 3, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 3, + -3 + ], + [ + 5, + -5 + ], + [ + 0, + -2 + ], + [ + 5, + -2 + ], + [ + 2, + 4 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 10.002911999999924, + 5.486915999999837, + 6.602254999999786, + 5.618484999999055, + 7.515418999999383, + 5.631263999999646, + 5.674248999999691 + ], + "timingMs": { + "min": 5.486915999999837, + "median": 5.674248999999691, + "p95": 10.002911999999924, + "max": 10.002911999999924 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", + "ratio": 0.9234368622156773, + "noiseRatio": 1.191579482731704, + "lowerRatio": 0.6922393548573168, + "upperRatio": 1.2318508511763249 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9234368622156773, + "noiseRatio": 1.191579482731704, + "lowerRatio": 0.6922393548573168, + "upperRatio": 1.2318508511763249, + "candidate": [ + 5.743753000000652, + 7.4569809999993595, + 5.275819000000411, + 6.521903999999267, + 6.599210000000312, + 5.40407100000084, + 5.326575999999477 + ], + "baseline": [ + 10.002911999999924, + 5.486915999999837, + 6.602254999999786, + 5.618484999999055, + 7.515418999999383, + 5.631263999999646, + 5.674248999999691 + ], + "selfA": [ + 6.268602000000101, + 5.488078000000314, + 5.292864999999438, + 7.186845000000176, + 5.845555999998396, + 5.5085889999991196, + 5.3529750000016065 + ], + "selfB": [ + 5.99640300000101, + 5.394156999998813, + 5.97029399999883, + 5.341488000000027, + 5.341818000000785, + 5.774810000000798, + 5.59995600000002 + ] + }, + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 1.0206785237529408, + "noiseRatio": 1.1590310329068552, + "lowerRatio": 0.9222803904271297, + "upperRatio": 1.12957475802777, + "candidate": [ + 5.9965529999990395, + 6.344356999999945, + 5.378031999998711, + 5.416981999998825, + 5.530692000000272, + 5.256609000000026, + 5.45744200000081 + ], + "baseline": [ + 5.416751000000659, + 5.263990999999805, + 5.859156000000439, + 5.325683999999455, + 5.595610000000306, + 5.674387999999453, + 5.393747000000076 + ], + "selfA": [ + 5.605606000000989, + 5.598875000001499, + 5.652105000001029, + 5.572736000000077, + 5.607557999999699, + 5.301447999998345, + 5.431673000000956 + ], + "selfB": [ + 5.335167999999612, + 5.529191000001447, + 5.311192000001029, + 5.4573920000002545, + 5.262998999998672, + 7.140935999999783, + 5.447738000000754 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 6.268602000000101, + 5.488078000000314, + 5.292864999999438, + 7.186845000000176, + 5.845555999998396, + 5.5085889999991196, + 5.3529750000016065 + ], + "baselineSelfB": [ + 5.99640300000101, + 5.394156999998813, + 5.97029399999883, + 5.341488000000027, + 5.341818000000785, + 5.774810000000798, + 5.59995600000002 + ] + }, + "historicalReference": { + "A": 20, + "U": 6, + "C": 75, + "plateSwitches": 19, + "commands": [ + [ + 0, + -2 + ], + [ + 4, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ], + [ + 5, + -6 + ], + [ + 3, + -3 + ], + [ + 2, + 2 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 0, + -5 + ], + [ + 5, + -5 + ], + [ + 4, + -5 + ], + [ + 1, + -5 + ], + [ + 3, + -3 + ], + [ + 0, + -2 + ], + [ + 5, + -2 + ], + [ + 2, + 4 + ], + [ + 4, + -6 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "voran-bedside-chest", + "title": "Voran's bedside chest", + "optimumA": 9, + "solvers": { + "originalBfs": { + "A": 9, + "U": 7, + "C": 38, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 4, + -1 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 3, + 6 + ], + [ + 5, + 6 + ], + [ + 3, + 6 + ], + [ + 6, + -5 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 199.1664579999997, + 190.00821799999903, + 208.4962169999999, + 197.6131600000008, + 198.21367400000054, + 217.74354200000016, + 204.622268000001 + ], + "timingMs": { + "min": 190.00821799999903, + "median": 199.1664579999997, + "p95": 217.74354200000016, + "max": 217.74354200000016 + } + }, + "upstream": { + "A": 9, + "U": 7, + "C": 38, + "plateSwitches": 8, + "commands": [ + [ + 4, + -1 + ], + [ + 6, + -5 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 3, + 6 + ], + [ + 5, + 6 + ], + [ + 3, + 6 + ], + [ + 4, + -3 + ], + [ + 1, + -2 + ] + ], + "samplesMs": [ + 178.98545000000013, + 180.83803799999987, + 188.3009579999998, + 184.55181599999923, + 196.82584599999973, + 192.62150900000051, + 219.23769600000014 + ], + "timingMs": { + "min": 178.98545000000013, + "median": 188.3009579999998, + "p95": 219.23769600000014, + "max": 219.23769600000014 + } + }, + "candidate": { + "A": 9, + "U": 7, + "C": 38, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 4, + -1 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 3, + 6 + ], + [ + 5, + 6 + ], + [ + 3, + 6 + ], + [ + 6, + -5 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 0.11362100000042119, + 0.1842980000001262, + 0.11203899999964051, + 0.11047600000165403, + 0.11176900000100431, + 0.11115699999936623, + 0.1573370000005525 + ], + "timingMs": { + "min": 0.11047600000165403, + "median": 0.11203899999964051, + "p95": 0.1842980000001262, + "max": 0.1842980000001262 + } + }, + "baseline": { + "A": 9, + "U": 7, + "C": 38, + "plateSwitches": 8, + "commands": [ + [ + 1, + -2 + ], + [ + 4, + -1 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 3, + 6 + ], + [ + 5, + 6 + ], + [ + 3, + 6 + ], + [ + 6, + -5 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 0.36556000000018685, + 0.16793300000063027, + 0.09411200000067765, + 0.09358099999917613, + 0.12914400000045134, + 0.09942899999987276, + 0.5305889999999636 + ], + "timingMs": { + "min": 0.09358099999917613, + "median": 0.12914400000045134, + "p95": 0.5305889999999636, + "max": 0.5305889999999636 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.7532149439120852, + "noiseRatio": 1.1275293681150074, + "lowerRatio": 0.3889327336371895, + "upperRatio": 1.4586911891600107 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.7532149439120852, + "noiseRatio": 1.1275293681150074, + "lowerRatio": 0.3889327336371895, + "upperRatio": 1.4586911891600107, + "candidate": [ + 0.11362100000042119, + 0.1842980000001262, + 0.11203899999964051, + 0.11047600000165403, + 0.11176900000100431, + 0.11115699999936623, + 0.1573370000005525 + ], + "baseline": [ + 0.36556000000018685, + 0.16793300000063027, + 0.09411200000067765, + 0.09358099999917613, + 0.12914400000045134, + 0.09942899999987276, + 0.5305889999999636 + ], + "selfA": [ + 0.08919400000013411, + 0.08680100000128732, + 0.0564650000014808, + 0.05620400000043446, + 0.04997499999990396, + 0.045178000000305474, + 0.04594900000120106 + ], + "selfB": [ + 0.09640500000023167, + 0.08885399999962829, + 0.046951000000262866, + 0.06075099999907252, + 0.04482800000005227, + 0.045087999998941086, + 0.04537800000071002 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.8888783261641711, + "noiseRatio": 1.787122069471407, + "lowerRatio": 0.6621500813350805, + "upperRatio": 1.1932410808307168, + "candidate": [ + 0.06457699999918987, + 0.0733909999999014, + 0.06271399999968708, + 0.06231300000035844, + 0.06481699999858392, + 0.05825700000059442, + 0.053641000000425265 + ], + "baseline": [ + 0.0720579999997426, + 0.05675599999995029, + 0.0934610000003886, + 0.0974869999990915, + 0.08617999999842141, + 0.04684099999940372, + 0.05688499999996566 + ], + "selfA": [ + 0.06630000000041036, + 0.17697700000098848, + 0.10446700000102283, + 0.06695099999888043, + 0.05578400000013062, + 0.04469699999935983, + 0.04814299999998184 + ], + "selfB": [ + 0.05961999999999534, + 0.07713599999988219, + 0.060921000000234926, + 0.0617130000009638, + 0.046731000000363565, + 0.04590900000039255, + 0.05319999999846914 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.08919400000013411, + 0.08680100000128732, + 0.0564650000014808, + 0.05620400000043446, + 0.04997499999990396, + 0.045178000000305474, + 0.04594900000120106 + ], + "baselineSelfB": [ + 0.09640500000023167, + 0.08885399999962829, + 0.046951000000262866, + 0.06075099999907252, + 0.04482800000005227, + 0.045087999998941086, + 0.04537800000071002 + ] + }, + "historicalReference": { + "A": 9, + "U": 7, + "C": 38, + "plateSwitches": 8, + "commands": [ + [ + 4, + -1 + ], + [ + 6, + -5 + ], + [ + 2, + -4 + ], + [ + 0, + 5 + ], + [ + 3, + 6 + ], + [ + 5, + 6 + ], + [ + 3, + 6 + ], + [ + 4, + -3 + ], + [ + 1, + -2 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "arto-room-door", + "title": "Door to Arto's room", + "optimumA": 7, + "solvers": { + "originalBfs": { + "A": 7, + "U": 5, + "C": 31, + "plateSwitches": 6, + "commands": [ + [ + 1, + -6 + ], + [ + 0, + -6 + ], + [ + 1, + -1 + ], + [ + 3, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + 5 + ], + [ + 2, + -3 + ] + ], + "samplesMs": [ + 3.1851729999998497, + 2.398339000001215, + 2.335194000001138, + 2.2831260000002658, + 3.200886000000537, + 2.3816029999998136, + 2.4000410000007832 + ], + "timingMs": { + "min": 2.2831260000002658, + "median": 2.398339000001215, + "p95": 3.200886000000537, + "max": 3.200886000000537 + } + }, + "upstream": { + "A": 7, + "U": 5, + "C": 31, + "plateSwitches": 6, + "commands": [ + [ + 1, + -6 + ], + [ + 0, + -5 + ], + [ + 3, + -5 + ], + [ + 0, + -6 + ], + [ + 4, + 5 + ], + [ + 2, + -3 + ], + [ + 1, + -1 + ] + ], + "samplesMs": [ + 4.736886999999115, + 4.581744999999501, + 4.6874729999999545, + 5.04921800000011, + 4.988276000000042, + 4.814645000000382, + 5.955120999999053 + ], + "timingMs": { + "min": 4.581744999999501, + "median": 4.814645000000382, + "p95": 5.955120999999053, + "max": 5.955120999999053 + } + }, + "candidate": { + "A": 7, + "U": 5, + "C": 31, + "plateSwitches": 6, + "commands": [ + [ + 1, + -6 + ], + [ + 0, + -6 + ], + [ + 1, + -1 + ], + [ + 3, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + 5 + ], + [ + 2, + -3 + ] + ], + "samplesMs": [ + 0.05984999999964202, + 0.04937400000017078, + 0.04800299999988056, + 0.05627499999900465, + 0.05228900000111025, + 0.0500850000007631, + 0.046920000000682194 + ], + "timingMs": { + "min": 0.046920000000682194, + "median": 0.0500850000007631, + "p95": 0.05984999999964202, + "max": 0.05984999999964202 + } + }, + "baseline": { + "A": 7, + "U": 5, + "C": 31, + "plateSwitches": 6, + "commands": [ + [ + 1, + -6 + ], + [ + 0, + -6 + ], + [ + 1, + -1 + ], + [ + 3, + -5 + ], + [ + 0, + -5 + ], + [ + 4, + 5 + ], + [ + 2, + -3 + ] + ], + "samplesMs": [ + 0.08412599999974191, + 0.04314499999964028, + 0.04024100000060571, + 0.052068000000872416, + 0.04796200000055251, + 0.0437660000006872, + 0.04570799999964947 + ], + "timingMs": { + "min": 0.04024100000060571, + "median": 0.04570799999964947, + "p95": 0.08412599999974191, + "max": 0.08412599999974191 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.0431734359770797, + "noiseRatio": 1.0796174793752589, + "lowerRatio": 0.8676100205792739, + "upperRatio": 1.2542626199748876 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0431734359770797, + "noiseRatio": 1.0796174793752589, + "lowerRatio": 0.8676100205792739, + "upperRatio": 1.2542626199748876, + "candidate": [ + 0.05984999999964202, + 0.04937400000017078, + 0.04800299999988056, + 0.05627499999900465, + 0.05228900000111025, + 0.0500850000007631, + 0.046920000000682194 + ], + "baseline": [ + 0.08412599999974191, + 0.04314499999964028, + 0.04024100000060571, + 0.052068000000872416, + 0.04796200000055251, + 0.0437660000006872, + 0.04570799999964947 + ], + "selfA": [ + 0.023726000001261127, + 0.022854999999253778, + 0.02294400000027963, + 0.022192999998878804, + 0.021392000000560074, + 0.023105000000214204, + 0.022123000000647153 + ], + "selfB": [ + 0.02638899999874411, + 0.02363599999989674, + 0.024046000000453205, + 0.02313500000127533, + 0.022304000000076485, + 0.022954999998546555, + 0.02207299999827228 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.1191980407051978, + "noiseRatio": 1.5737848701889292, + "lowerRatio": 1.0124621924834014, + "upperRatio": 1.2371862017345299, + "candidate": [ + 0.028372000000672415, + 0.032369000000471715, + 0.02709099999992759, + 0.025929000001269742, + 0.027221000000281492, + 0.027211000000534113, + 0.024817999999868334 + ], + "baseline": [ + 0.026841000000786153, + 0.02404699999897275, + 0.025529000000460655, + 0.025057999999262393, + 0.023436000001311186, + 0.023344999999608262, + 0.02386599999954342 + ], + "selfA": [ + 0.02581800000007206, + 0.023265000001629232, + 0.025829000000157976, + 0.026230000001305598, + 0.024676999999428517, + 0.02322499999900174, + 0.02371600000151375 + ], + "selfB": [ + 0.061432000000422704, + 0.022504000000481028, + 0.02480699999978242, + 0.024687000000994885, + 0.022753999999622465, + 0.02456700000038836, + 0.022234000000025844 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.023726000001261127, + 0.022854999999253778, + 0.02294400000027963, + 0.022192999998878804, + 0.021392000000560074, + 0.023105000000214204, + 0.022123000000647153 + ], + "baselineSelfB": [ + 0.02638899999874411, + 0.02363599999989674, + 0.024046000000453205, + 0.02313500000127533, + 0.022304000000076485, + 0.022954999998546555, + 0.02207299999827228 + ] + }, + "historicalReference": { + "A": 7, + "U": 5, + "C": 31, + "plateSwitches": 6, + "commands": [ + [ + 1, + -6 + ], + [ + 0, + -5 + ], + [ + 3, + -5 + ], + [ + 0, + -6 + ], + [ + 4, + 5 + ], + [ + 2, + -3 + ], + [ + 1, + -1 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "arto-far-bed-chest", + "title": "Arto's far bed chest", + "optimumA": 11, + "solvers": { + "originalBfs": { + "A": 11, + "U": 5, + "C": 44, + "plateSwitches": 10, + "commands": [ + [ + 1, + 4 + ], + [ + 2, + -2 + ], + [ + 5, + -2 + ], + [ + 3, + 3 + ], + [ + 4, + 6 + ], + [ + 1, + 6 + ], + [ + 5, + -6 + ], + [ + 4, + 6 + ], + [ + 1, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 84.36533000000054, + 86.1215229999998, + 86.2261209999997, + 84.34919599999921, + 89.98912399999972, + 88.35752800000046, + 87.5321159999985 + ], + "timingMs": { + "min": 84.34919599999921, + "median": 86.2261209999997, + "p95": 89.98912399999972, + "max": 89.98912399999972 + } + }, + "upstream": { + "A": 13, + "U": 5, + "C": 40, + "plateSwitches": 12, + "commands": [ + [ + 3, + 1 + ], + [ + 4, + 2 + ], + [ + 5, + -2 + ], + [ + 3, + 2 + ], + [ + 1, + 4 + ], + [ + 4, + 4 + ], + [ + 5, + -3 + ], + [ + 3, + 3 + ], + [ + 2, + 1 + ], + [ + 1, + 6 + ], + [ + 4, + 6 + ], + [ + 5, + -3 + ], + [ + 1, + 3 + ] + ], + "samplesMs": [ + 32.24119299999984, + 27.58856100000048, + 27.363272000000507, + 27.101126999999906, + 27.185574999999517, + 28.424409000002925, + 31.55996800000139 + ], + "timingMs": { + "min": 27.101126999999906, + "median": 27.58856100000048, + "p95": 32.24119299999984, + "max": 32.24119299999984 + } + }, + "candidate": { + "A": 11, + "U": 5, + "C": 44, + "plateSwitches": 10, + "commands": [ + [ + 1, + 4 + ], + [ + 2, + -2 + ], + [ + 5, + -2 + ], + [ + 3, + 3 + ], + [ + 4, + 6 + ], + [ + 1, + 6 + ], + [ + 5, + -6 + ], + [ + 4, + 6 + ], + [ + 1, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.16762200000084704, + 0.17408199999954377, + 0.16566899999997986, + 0.16762199999902805, + 0.49568600000020524, + 0.164677999997366, + 0.16835300000093412 + ], + "timingMs": { + "min": 0.164677999997366, + "median": 0.16762200000084704, + "p95": 0.49568600000020524, + "max": 0.49568600000020524 + } + }, + "baseline": { + "A": 11, + "U": 5, + "C": 44, + "plateSwitches": 10, + "commands": [ + [ + 1, + 4 + ], + [ + 2, + -2 + ], + [ + 5, + -2 + ], + [ + 3, + 3 + ], + [ + 4, + 6 + ], + [ + 1, + 6 + ], + [ + 5, + -6 + ], + [ + 4, + 6 + ], + [ + 1, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.15266999999948894, + 0.15373100000033446, + 0.19375199999922188, + 0.1529500000015105, + 0.1573370000005525, + 0.1555740000003425, + 0.17360099999859813 + ], + "timingMs": { + "min": 0.15266999999948894, + "median": 0.1555740000003425, + "p95": 0.19375199999922188, + "max": 0.19375199999922188 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.2086442024743995, + "noiseRatio": 1.170322194976676, + "lowerRatio": 0.7670604680257382, + "upperRatio": 1.904440222209392 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.2086442024743995, + "noiseRatio": 1.170322194976676, + "lowerRatio": 0.7670604680257382, + "upperRatio": 1.904440222209392, + "candidate": [ + 0.16762200000084704, + 0.17408199999954377, + 0.16566899999997986, + 0.16762199999902805, + 0.49568600000020524, + 0.164677999997366, + 0.16835300000093412 + ], + "baseline": [ + 0.15266999999948894, + 0.15373100000033446, + 0.19375199999922188, + 0.1529500000015105, + 0.1573370000005525, + 0.1555740000003425, + 0.17360099999859813 + ], + "selfA": [ + 0.10941400000046997, + 0.09374099999877217, + 0.09103700000014214, + 0.10444699999970908, + 0.09672599999976228, + 0.08792199999879813, + 0.08673099999941769 + ], + "selfB": [ + 0.09156799999982468, + 0.1093540000001667, + 0.09035599999879196, + 0.08916500000123051, + 0.0903359999992972, + 0.10735199999908218, + 0.08832299999994575 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.116743880627492, + "noiseRatio": 4.782839047414779, + "lowerRatio": 0.7491885357259764, + "upperRatio": 1.6646235699675684, + "candidate": [ + 0.12749199999962002, + 0.13141800000084913, + 0.2237359999999171, + 0.11998999999923399, + 0.11928900000202702, + 0.1806719999985944, + 0.38905999999769847 + ], + "baseline": [ + 0.12363600000026054, + 0.09112699999968754, + 0.343576999999641, + 0.12779199999931734, + 0.14758200000142097, + 0.10246399999959976, + 0.23276999999870895 + ], + "selfA": [ + 0.11450200000035693, + 0.09765700000207289, + 0.1141910000005737, + 0.10691099999894504, + 0.11067600000023958, + 0.09602499999891734, + 0.28599000000031083 + ], + "selfB": [ + 0.09966999999960535, + 0.17578499999945052, + 1.8510360000000219, + 0.12241400000129943, + 0.10491800000090734, + 0.09441199999855598, + 0.11467199999970035 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.10941400000046997, + 0.09374099999877217, + 0.09103700000014214, + 0.10444699999970908, + 0.09672599999976228, + 0.08792199999879813, + 0.08673099999941769 + ], + "baselineSelfB": [ + 0.09156799999982468, + 0.1093540000001667, + 0.09035599999879196, + 0.08916500000123051, + 0.0903359999992972, + 0.10735199999908218, + 0.08832299999994575 + ] + }, + "historicalReference": { + "A": 13, + "U": 5, + "C": 40, + "plateSwitches": 12, + "commands": [ + [ + 3, + 1 + ], + [ + 4, + 2 + ], + [ + 5, + -2 + ], + [ + 3, + 2 + ], + [ + 1, + 4 + ], + [ + 4, + 4 + ], + [ + 5, + -3 + ], + [ + 3, + 3 + ], + [ + 2, + 1 + ], + [ + 1, + 6 + ], + [ + 4, + 6 + ], + [ + 5, + -3 + ], + [ + 1, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "regression", + "deltas": { + "A": -2, + "U": 0, + "C": 4, + "plateSwitches": -2 + }, + "regressions": [ + "C" + ] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "arto-near-bed-chest", + "title": "Arto's near bed chest", + "optimumA": 13, + "solvers": { + "originalBfs": { + "A": 13, + "U": 6, + "C": 52, + "plateSwitches": 12, + "commands": [ + [ + 4, + -1 + ], + [ + 0, + 6 + ], + [ + 3, + 2 + ], + [ + 2, + 4 + ], + [ + 5, + -3 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + 6 + ], + [ + 3, + 6 + ], + [ + 2, + 6 + ], + [ + 5, + -6 + ], + [ + 1, + -3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 93.48326200000156, + 99.21483900000021, + 91.84003999999914, + 114.51992299999984, + 114.96519800000169, + 105.04207799999858, + 116.20399300000281 + ], + "timingMs": { + "min": 91.84003999999914, + "median": 105.04207799999858, + "p95": 116.20399300000281, + "max": 116.20399300000281 + } + }, + "upstream": { + "A": 13, + "U": 6, + "C": 52, + "plateSwitches": 12, + "commands": [ + [ + 4, + -1 + ], + [ + 5, + -3 + ], + [ + 3, + 4 + ], + [ + 0, + 6 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 2, + 4 + ], + [ + 3, + 4 + ], + [ + 5, + -6 + ], + [ + 2, + 6 + ], + [ + 3, + 3 + ], + [ + 0, + 6 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 72.72948800000086, + 70.43284199999835, + 84.94343099999969, + 75.99135699999897, + 76.52235699999801, + 102.69168100000024, + 76.19374199999947 + ], + "timingMs": { + "min": 70.43284199999835, + "median": 76.19374199999947, + "p95": 102.69168100000024, + "max": 102.69168100000024 + } + }, + "candidate": { + "A": 13, + "U": 6, + "C": 52, + "plateSwitches": 12, + "commands": [ + [ + 4, + -1 + ], + [ + 0, + 6 + ], + [ + 5, + -3 + ], + [ + 3, + 4 + ], + [ + 2, + 6 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 3, + 4 + ], + [ + 0, + 6 + ], + [ + 2, + 4 + ], + [ + 5, + -6 + ], + [ + 1, + -3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.44626200000129757, + 0.44416800000180956, + 0.4871129999992263, + 0.44914600000265636, + 0.4682539999994333, + 0.46663300000000163, + 0.44776400000046124 + ], + "timingMs": { + "min": 0.44416800000180956, + "median": 0.44914600000265636, + "p95": 0.4871129999992263, + "max": 0.4871129999992263 + } + }, + "baseline": { + "A": 13, + "U": 6, + "C": 52, + "plateSwitches": 12, + "commands": [ + [ + 4, + -1 + ], + [ + 0, + 6 + ], + [ + 5, + -3 + ], + [ + 3, + 4 + ], + [ + 2, + 6 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 3, + 4 + ], + [ + 0, + 6 + ], + [ + 2, + 4 + ], + [ + 5, + -6 + ], + [ + 1, + -3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.4818750000013097, + 0.47723799999948824, + 0.44000299999970593, + 0.4739029999982449, + 0.4392410000000382, + 0.750529999997525, + 0.5789120000008552 + ], + "timingMs": { + "min": 0.4392410000000382, + "median": 0.47723799999948824, + "p95": 0.750529999997525, + "max": 0.750529999997525 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.8960019137269032, + "noiseRatio": 1.536030275362091, + "lowerRatio": 0.7277393996588619, + "upperRatio": 1.1031688400801245 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.8960019137269032, + "noiseRatio": 1.536030275362091, + "lowerRatio": 0.7277393996588619, + "upperRatio": 1.1031688400801245, + "candidate": [ + 0.44626200000129757, + 0.44416800000180956, + 0.4871129999992263, + 0.44914600000265636, + 0.4682539999994333, + 0.46663300000000163, + 0.44776400000046124 + ], + "baseline": [ + 0.4818750000013097, + 0.47723799999948824, + 0.44000299999970593, + 0.4739029999982449, + 0.4392410000000382, + 0.750529999997525, + 0.5789120000008552 + ], + "selfA": [ + 0.48795500000051106, + 0.7330430000001797, + 0.39147899999807123, + 0.5262120000006689, + 0.383257000001322, + 0.3927210000001651, + 0.36360699999931967 + ], + "selfB": [ + 0.44363700000030803, + 0.6561079999992216, + 0.5254009999989648, + 0.9030800000000454, + 0.5744040000026871, + 0.393011000000115, + 0.40115399999922374 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.011465653312673, + "noiseRatio": 1.4316363830277896, + "lowerRatio": 0.5961884217670623, + "upperRatio": 1.7160057634110757, + "candidate": [ + 0.42741300000125193, + 0.5093459999989136, + 0.3987199999974109, + 1.0375119999989693, + 0.4779290000005858, + 0.47367300000041723, + 0.42971699999907287 + ], + "baseline": [ + 0.44182500000169966, + 0.41073900000264985, + 0.9937959999988379, + 0.4736629999970319, + 0.4423960000021907, + 0.5372089999982563, + 0.3984290000007604 + ], + "selfA": [ + 0.4276540000028035, + 0.6434589999989839, + 0.4788410000001022, + 0.7158769999987271, + 0.39983199999915087, + 0.42971599999873433, + 0.38802399999985937 + ], + "selfB": [ + 0.37017700000069453, + 0.5140929999979562, + 0.45360299999811104, + 0.4186599999993632, + 0.3655299999991257, + 0.3625659999997879, + 0.382846000000427 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.48795500000051106, + 0.7330430000001797, + 0.39147899999807123, + 0.5262120000006689, + 0.383257000001322, + 0.3927210000001651, + 0.36360699999931967 + ], + "baselineSelfB": [ + 0.44363700000030803, + 0.6561079999992216, + 0.5254009999989648, + 0.9030800000000454, + 0.5744040000026871, + 0.393011000000115, + 0.40115399999922374 + ] + }, + "historicalReference": { + "A": 13, + "U": 6, + "C": 52, + "plateSwitches": 12, + "commands": [ + [ + 4, + -1 + ], + [ + 5, + -3 + ], + [ + 3, + 4 + ], + [ + 0, + 6 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 2, + 4 + ], + [ + 3, + 4 + ], + [ + 5, + -6 + ], + [ + 2, + 6 + ], + [ + 3, + 3 + ], + [ + 0, + 6 + ], + [ + 1, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "gomez-bedrooms-door", + "title": "Door on Gomez's bedrooms floor", + "optimumA": 14, + "solvers": { + "originalBfs": { + "A": 14, + "U": 6, + "C": 45, + "plateSwitches": 13, + "commands": [ + [ + 0, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + -6 + ], + [ + 1, + 3 + ], + [ + 0, + 4 + ], + [ + 2, + 4 + ], + [ + 3, + -3 + ], + [ + 4, + 5 + ], + [ + 0, + 4 + ], + [ + 1, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 4 + ], + [ + 0, + 3 + ], + [ + 5, + 1 + ] + ], + "samplesMs": [ + 106.72043499999927, + 101.2885090000018, + 104.10252500000206, + 110.61321400000088, + 102.18899500000043, + 101.30750800000169, + 104.96197799999936 + ], + "timingMs": { + "min": 101.2885090000018, + "median": 104.10252500000206, + "p95": 110.61321400000088, + "max": 110.61321400000088 + } + }, + "upstream": { + "A": 14, + "U": 6, + "C": 45, + "plateSwitches": 13, + "commands": [ + [ + 3, + -3 + ], + [ + 0, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 3 + ], + [ + 0, + 4 + ], + [ + 4, + 4 + ], + [ + 2, + 4 + ], + [ + 0, + 4 + ], + [ + 5, + 1 + ], + [ + 4, + 5 + ], + [ + 3, + -6 + ], + [ + 2, + 3 + ], + [ + 1, + 3 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 37.79504199999792, + 41.81860699999743, + 36.81990399999995, + 38.27305100000012, + 38.40403800000058, + 38.76095499999792, + 40.954696000000695 + ], + "timingMs": { + "min": 36.81990399999995, + "median": 38.40403800000058, + "p95": 41.81860699999743, + "max": 41.81860699999743 + } + }, + "candidate": { + "A": 14, + "U": 6, + "C": 49, + "plateSwitches": 13, + "commands": [ + [ + 3, + -6 + ], + [ + 5, + -2 + ], + [ + 2, + 2 + ], + [ + 0, + 3 + ], + [ + 1, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + 3 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 5, + 3 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 5.996425000001182, + 7.218203000000358, + 5.7388670000000275, + 6.5628969999997935, + 5.623243999998522, + 5.476120999999694, + 5.6087320000006 + ], + "timingMs": { + "min": 5.476120999999694, + "median": 5.7388670000000275, + "p95": 7.218203000000358, + "max": 7.218203000000358 + } + }, + "baseline": { + "A": 14, + "U": 6, + "C": 49, + "plateSwitches": 13, + "commands": [ + [ + 3, + -6 + ], + [ + 5, + -2 + ], + [ + 2, + 2 + ], + [ + 0, + 3 + ], + [ + 1, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + 3 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 5, + 3 + ], + [ + 0, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 6.136445000000094, + 5.7011100000017905, + 8.624781000002258, + 6.272659999998723, + 5.65148599999884, + 5.458033999999316, + 5.763394000001426 + ], + "timingMs": { + "min": 5.458033999999316, + "median": 5.763394000001426, + "p95": 8.624781000002258, + "max": 8.624781000002258 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.9748660442098722, + "noiseRatio": 1.0587583552979651, + "lowerRatio": 0.7972629533021802, + "upperRatio": 1.1920330679070141 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9748660442098722, + "noiseRatio": 1.0587583552979651, + "lowerRatio": 0.7972629533021802, + "upperRatio": 1.1920330679070141, + "candidate": [ + 5.996425000001182, + 7.218203000000358, + 5.7388670000000275, + 6.5628969999997935, + 5.623243999998522, + 5.476120999999694, + 5.6087320000006 + ], + "baseline": [ + 6.136445000000094, + 5.7011100000017905, + 8.624781000002258, + 6.272659999998723, + 5.65148599999884, + 5.458033999999316, + 5.763394000001426 + ], + "selfA": [ + 5.50688799999989, + 5.357202999999572, + 5.406025999996928, + 5.5422100000032515, + 6.047120999999606, + 5.859859999996843, + 5.612536999997246 + ], + "selfB": [ + 5.416801999999734, + 5.783855000001495, + 5.405395999998291, + 5.651635999998689, + 5.6948410000004515, + 5.588550000000396, + 5.437733999999182 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0102552348988454, + "noiseRatio": 1.1308450438053403, + "lowerRatio": 0.8361241871224652, + "upperRatio": 1.2206507781493396, + "candidate": [ + 6.0461189999987255, + 7.764476000000286, + 5.547810000000027, + 5.909584000000905, + 5.314168000000791, + 5.59868600000118, + 5.312254999997094 + ], + "baseline": [ + 5.583944000001793, + 5.4918350000007194, + 5.776493999997911, + 5.521318999999494, + 6.0751430000018445, + 6.903519999999844, + 5.521500000002561 + ], + "selfA": [ + 5.559316999999282, + 5.7145500000005995, + 5.912598999999318, + 5.657123000000865, + 5.510543000000325, + 5.955432999999175, + 5.640048000001116 + ], + "selfB": [ + 5.486466999998811, + 5.563733999999386, + 6.504758999999467, + 6.636227999999392, + 5.643724000001384, + 6.5993019999987155, + 5.656123000000662 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 5.50688799999989, + 5.357202999999572, + 5.406025999996928, + 5.5422100000032515, + 6.047120999999606, + 5.859859999996843, + 5.612536999997246 + ], + "baselineSelfB": [ + 5.416801999999734, + 5.783855000001495, + 5.405395999998291, + 5.651635999998689, + 5.6948410000004515, + 5.588550000000396, + 5.437733999999182 + ] + }, + "historicalReference": { + "A": 14, + "U": 6, + "C": 45, + "plateSwitches": 13, + "commands": [ + [ + 3, + -3 + ], + [ + 0, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 3 + ], + [ + 0, + 4 + ], + [ + 4, + 4 + ], + [ + 2, + 4 + ], + [ + 0, + 4 + ], + [ + 5, + 1 + ], + [ + 4, + 5 + ], + [ + 3, + -6 + ], + [ + 2, + 3 + ], + [ + 1, + 3 + ], + [ + 0, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "regression", + "deltas": { + "A": 0, + "U": 0, + "C": 4, + "plateSwitches": 0 + }, + "regressions": [ + "C" + ] + }, + "upstream": { + "quality": "regression", + "deltas": { + "A": 0, + "U": 0, + "C": 4, + "plateSwitches": 0 + }, + "regressions": [ + "C" + ] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "gomez-bedrooms-far-chest", + "title": "Far bed chest (bedrooms floor)", + "optimumA": 14, + "solvers": { + "originalBfs": { + "A": 14, + "U": 5, + "C": 43, + "plateSwitches": 13, + "commands": [ + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + -3 + ], + [ + 2, + -2 + ], + [ + 1, + -2 + ], + [ + 3, + 4 + ], + [ + 2, + -5 + ], + [ + 0, + -4 + ], + [ + 1, + -2 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 11.207657999999356, + 15.987766999998712, + 11.984307000002445, + 13.01028999999835, + 10.965552999998181, + 10.679371999998693, + 10.33407299999817 + ], + "timingMs": { + "min": 10.33407299999817, + "median": 11.207657999999356, + "p95": 15.987766999998712, + "max": 15.987766999998712 + } + }, + "upstream": { + "A": 16, + "U": 5, + "C": 39, + "plateSwitches": 15, + "commands": [ + [ + 0, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + -1 + ], + [ + 3, + 1 + ], + [ + 2, + -1 + ], + [ + 4, + -2 + ], + [ + 2, + -2 + ], + [ + 1, + -2 + ], + [ + 3, + 4 + ], + [ + 2, + -4 + ], + [ + 3, + 4 + ], + [ + 2, + -2 + ], + [ + 4, + -4 + ], + [ + 2, + -3 + ], + [ + 0, + -5 + ], + [ + 1, + -2 + ] + ], + "samplesMs": [ + 6.223947000002227, + 5.5543890000008105, + 6.192258999999467, + 5.621289999999135, + 6.539220999999088, + 5.584665000002133, + 5.526977999998053 + ], + "timingMs": { + "min": 5.526977999998053, + "median": 5.621289999999135, + "p95": 6.539220999999088, + "max": 6.539220999999088 + } + }, + "candidate": { + "A": 14, + "U": 5, + "C": 43, + "plateSwitches": 13, + "commands": [ + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 3, + 2 + ], + [ + 4, + -3 + ], + [ + 2, + -4 + ], + [ + 0, + -5 + ], + [ + 1, + -4 + ], + [ + 0, + -2 + ], + [ + 3, + 2 + ], + [ + 2, + -3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 1.5610300000007555, + 1.3212390000007872, + 2.1247980000007374, + 1.5893009999999776, + 1.3724660000007134, + 1.3105820000018866, + 1.3346980000023905 + ], + "timingMs": { + "min": 1.3105820000018866, + "median": 1.3724660000007134, + "p95": 2.1247980000007374, + "max": 2.1247980000007374 + } + }, + "baseline": { + "A": 14, + "U": 5, + "C": 43, + "plateSwitches": 13, + "commands": [ + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 3, + 2 + ], + [ + 4, + -3 + ], + [ + 2, + -4 + ], + [ + 0, + -5 + ], + [ + 1, + -4 + ], + [ + 0, + -2 + ], + [ + 3, + 2 + ], + [ + 2, + -3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 1.5528970000013942, + 1.537133999998332, + 2.3746179999980086, + 1.6453760000003967, + 1.4976739999983693, + 1.6183649999984482, + 1.537694000002375 + ], + "timingMs": { + "min": 1.4976739999983693, + "median": 1.5528970000013942, + "p95": 2.3746179999980086, + "max": 2.3746179999980086 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.9007376641859475, + "noiseRatio": 1.1068051486703363, + "lowerRatio": 0.8341419560467856, + "upperRatio": 0.9726501991678389 + }, + "timingAttempts": [ + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.9007376641859475, + "noiseRatio": 1.1068051486703363, + "lowerRatio": 0.8341419560467856, + "upperRatio": 0.9726501991678389, + "candidate": [ + 1.5610300000007555, + 1.3212390000007872, + 2.1247980000007374, + 1.5893009999999776, + 1.3724660000007134, + 1.3105820000018866, + 1.3346980000023905 + ], + "baseline": [ + 1.5528970000013942, + 1.537133999998332, + 2.3746179999980086, + 1.6453760000003967, + 1.4976739999983693, + 1.6183649999984482, + 1.537694000002375 + ], + "selfA": [ + 1.3812289999987115, + 1.5048850000021048, + 1.3781239999989339, + 1.5837439999995695, + 1.4039530000009108, + 1.347647999999026, + 1.3802670000004582 + ], + "selfB": [ + 1.322570000000269, + 1.354457999997976, + 1.4024810000009893, + 1.4063370000003488, + 1.3295709999983956, + 1.3724760000004608, + 1.3401770000018587 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 1.3812289999987115, + 1.5048850000021048, + 1.3781239999989339, + 1.5837439999995695, + 1.4039530000009108, + 1.347647999999026, + 1.3802670000004582 + ], + "baselineSelfB": [ + 1.322570000000269, + 1.354457999997976, + 1.4024810000009893, + 1.4063370000003488, + 1.3295709999983956, + 1.3724760000004608, + 1.3401770000018587 + ] + }, + "historicalReference": { + "A": 16, + "U": 5, + "C": 39, + "plateSwitches": 15, + "commands": [ + [ + 0, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + -1 + ], + [ + 3, + 1 + ], + [ + 2, + -1 + ], + [ + 4, + -2 + ], + [ + 2, + -2 + ], + [ + 1, + -2 + ], + [ + 3, + 4 + ], + [ + 2, + -4 + ], + [ + 3, + 4 + ], + [ + 2, + -2 + ], + [ + 4, + -4 + ], + [ + 2, + -3 + ], + [ + 0, + -5 + ], + [ + 1, + -2 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "regression", + "deltas": { + "A": -2, + "U": 0, + "C": 4, + "plateSwitches": -2 + }, + "regressions": [ + "C" + ] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "gomez-bedrooms-near-chest", + "title": "Near bed chest (bedrooms floor)", + "optimumA": 12, + "solvers": { + "originalBfs": { + "A": 12, + "U": 6, + "C": 56, + "plateSwitches": 11, + "commands": [ + [ + 0, + 4 + ], + [ + 2, + 1 + ], + [ + 5, + -6 + ], + [ + 4, + -6 + ], + [ + 0, + 6 + ], + [ + 5, + -6 + ], + [ + 1, + -6 + ], + [ + 0, + 6 + ], + [ + 3, + -3 + ], + [ + 0, + 3 + ], + [ + 5, + -6 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 75.92996499999936, + 72.20332599999892, + 83.12275500000032, + 89.42843999999968, + 79.52953200000047, + 76.69865199999913, + 78.9937460000001 + ], + "timingMs": { + "min": 72.20332599999892, + "median": 78.9937460000001, + "p95": 89.42843999999968, + "max": 89.42843999999968 + } + }, + "upstream": { + "A": 12, + "U": 6, + "C": 56, + "plateSwitches": 11, + "commands": [ + [ + 5, + -6 + ], + [ + 0, + 4 + ], + [ + 4, + -6 + ], + [ + 5, + -6 + ], + [ + 2, + 1 + ], + [ + 0, + 6 + ], + [ + 1, + -6 + ], + [ + 5, + -6 + ], + [ + 0, + 6 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 43.113225000000966, + 44.942173999999795, + 44.68240700000024, + 42.28151400000206, + 43.22101299999849, + 43.647359000002325, + 49.91504200000054 + ], + "timingMs": { + "min": 42.28151400000206, + "median": 43.647359000002325, + "p95": 49.91504200000054, + "max": 49.91504200000054 + } + }, + "candidate": { + "A": 12, + "U": 6, + "C": 56, + "plateSwitches": 11, + "commands": [ + [ + 0, + 4 + ], + [ + 2, + 1 + ], + [ + 5, + -6 + ], + [ + 4, + -6 + ], + [ + 0, + 6 + ], + [ + 5, + -6 + ], + [ + 1, + -6 + ], + [ + 0, + 6 + ], + [ + 3, + -3 + ], + [ + 0, + 3 + ], + [ + 5, + -6 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 0.10293500000261702, + 0.09918000000106986, + 0.10355600000184495, + 0.10368599999856087, + 0.09805799999958253, + 0.0970660000020871, + 0.09674600000289502 + ], + "timingMs": { + "min": 0.09674600000289502, + "median": 0.09918000000106986, + "p95": 0.10368599999856087, + "max": 0.10368599999856087 + } + }, + "baseline": { + "A": 12, + "U": 6, + "C": 56, + "plateSwitches": 11, + "commands": [ + [ + 0, + 4 + ], + [ + 2, + 1 + ], + [ + 5, + -6 + ], + [ + 4, + -6 + ], + [ + 0, + 6 + ], + [ + 5, + -6 + ], + [ + 1, + -6 + ], + [ + 0, + 6 + ], + [ + 3, + -3 + ], + [ + 0, + 3 + ], + [ + 5, + -6 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 0.08027999999831081, + 0.08136200000080862, + 0.11122799999793642, + 0.4359660000009171, + 0.15506299999833573, + 0.10341499999776715, + 0.11153799999738112 + ], + "timingMs": { + "min": 0.08027999999831081, + "median": 0.11122799999793642, + "p95": 0.4359660000009171, + "max": 0.4359660000009171 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.7815906384700172, + "noiseRatio": 1.2514474655499743, + "lowerRatio": 0.427905955251845, + "upperRatio": 1.4276125832005118 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.7815906384700172, + "noiseRatio": 1.2514474655499743, + "lowerRatio": 0.427905955251845, + "upperRatio": 1.4276125832005118, + "candidate": [ + 0.10293500000261702, + 0.09918000000106986, + 0.10355600000184495, + 0.10368599999856087, + 0.09805799999958253, + 0.0970660000020871, + 0.09674600000289502 + ], + "baseline": [ + 0.08027999999831081, + 0.08136200000080862, + 0.11122799999793642, + 0.4359660000009171, + 0.15506299999833573, + 0.10341499999776715, + 0.11153799999738112 + ], + "selfA": [ + 0.04046100000050501, + 0.03728599999885773, + 0.0637959999985469, + 0.04100200000175391, + 0.03611399999863352, + 0.04031100000065635, + 0.03777700000136974 + ], + "selfB": [ + 0.04563899999993737, + 0.038969000001088716, + 0.04062100000010105, + 0.040140000000974396, + 0.03786699999909615, + 0.04299399999945308, + 0.03994999999849824 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9995119994496934, + "noiseRatio": 1.2666966883771833, + "lowerRatio": 0.8279734422127132, + "upperRatio": 1.206589711831924, + "candidate": [ + 0.046839999999065185, + 0.043164999999135034, + 0.04465699999855133, + 0.04624999999941792, + 0.044226999998500105, + 0.04964499999914551, + 0.0484230000001844 + ], + "baseline": [ + 0.04392600000210223, + 0.04267500000059954, + 0.04642999999850872, + 0.045588999997562496, + 0.06316499999957159, + 0.03918899999916903, + 0.04535799999939627 + ], + "selfA": [ + 0.042223999997077044, + 0.04125199999907636, + 0.040660999999090564, + 0.040460000000166474, + 0.04225299999961862, + 0.04138199999943026, + 0.06836300000213669 + ], + "selfB": [ + 0.04063099999984843, + 0.038908999998966465, + 0.04013999999733642, + 0.04837300000144751, + 0.03929900000002817, + 0.04409599999780767, + 0.045709000001807 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.04046100000050501, + 0.03728599999885773, + 0.0637959999985469, + 0.04100200000175391, + 0.03611399999863352, + 0.04031100000065635, + 0.03777700000136974 + ], + "baselineSelfB": [ + 0.04563899999993737, + 0.038969000001088716, + 0.04062100000010105, + 0.040140000000974396, + 0.03786699999909615, + 0.04299399999945308, + 0.03994999999849824 + ] + }, + "historicalReference": { + "A": 12, + "U": 6, + "C": 56, + "plateSwitches": 11, + "commands": [ + [ + 5, + -6 + ], + [ + 0, + 4 + ], + [ + 4, + -6 + ], + [ + 5, + -6 + ], + [ + 2, + 1 + ], + [ + 0, + 6 + ], + [ + 1, + -6 + ], + [ + 5, + -6 + ], + [ + 0, + 6 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 0, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "raven-tower-left-chest", + "title": "Raven Tower left chest", + "optimumA": 11, + "solvers": { + "originalBfs": { + "A": 11, + "U": 6, + "C": 35, + "plateSwitches": 10, + "commands": [ + [ + 1, + -5 + ], + [ + 2, + -1 + ], + [ + 3, + 2 + ], + [ + 0, + 2 + ], + [ + 3, + 2 + ], + [ + 4, + 4 + ], + [ + 5, + -4 + ], + [ + 0, + 6 + ], + [ + 2, + -3 + ], + [ + 3, + 3 + ], + [ + 4, + 3 + ] + ], + "samplesMs": [ + 128.53769000000102, + 118.29718199999843, + 116.36731299999883, + 108.86097200000222, + 109.44556999999986, + 108.36314700000003, + 106.78033499999947 + ], + "timingMs": { + "min": 106.78033499999947, + "median": 109.44556999999986, + "p95": 128.53769000000102, + "max": 128.53769000000102 + } + }, + "upstream": { + "A": 11, + "U": 6, + "C": 35, + "plateSwitches": 10, + "commands": [ + [ + 5, + -3 + ], + [ + 4, + 1 + ], + [ + 3, + 2 + ], + [ + 0, + 5 + ], + [ + 2, + -4 + ], + [ + 5, + -1 + ], + [ + 4, + 6 + ], + [ + 1, + -2 + ], + [ + 3, + 5 + ], + [ + 1, + -3 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 50.84226800000033, + 53.75917699999991, + 52.739472999997815, + 53.35408800000005, + 53.098704, + 50.198529000001145, + 50.16471799999999 + ], + "timingMs": { + "min": 50.16471799999999, + "median": 52.739472999997815, + "p95": 53.75917699999991, + "max": 53.75917699999991 + } + }, + "candidate": { + "A": 11, + "U": 6, + "C": 35, + "plateSwitches": 10, + "commands": [ + [ + 4, + 2 + ], + [ + 5, + -3 + ], + [ + 3, + 2 + ], + [ + 0, + 5 + ], + [ + 2, + -4 + ], + [ + 4, + 5 + ], + [ + 5, + -1 + ], + [ + 1, + -3 + ], + [ + 3, + 5 + ], + [ + 0, + 3 + ], + [ + 1, + -2 + ] + ], + "samplesMs": [ + 1.825286999999662, + 1.4545190000026196, + 1.3061560000023746, + 1.358064000000013, + 1.3477190000012342, + 1.7332279999973252, + 1.6534879999999248 + ], + "timingMs": { + "min": 1.3061560000023746, + "median": 1.4545190000026196, + "p95": 1.825286999999662, + "max": 1.825286999999662 + } + }, + "baseline": { + "A": 11, + "U": 6, + "C": 35, + "plateSwitches": 10, + "commands": [ + [ + 4, + 2 + ], + [ + 5, + -3 + ], + [ + 3, + 2 + ], + [ + 0, + 5 + ], + [ + 2, + -4 + ], + [ + 4, + 5 + ], + [ + 5, + -1 + ], + [ + 1, + -3 + ], + [ + 3, + 5 + ], + [ + 0, + 3 + ], + [ + 1, + -2 + ] + ], + "samplesMs": [ + 1.441689999999653, + 1.3271779999995488, + 1.8665989999972226, + 1.3084890000027372, + 1.3080389999995532, + 1.34014599999864, + 1.374098000000231 + ], + "timingMs": { + "min": 1.3080389999995532, + "median": 1.34014599999864, + "p95": 1.8665989999972226, + "max": 1.8665989999972226 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.0709587699984509, + "noiseRatio": 1.1937145569796526, + "lowerRatio": 0.860353591160936, + "upperRatio": 1.3331178004254396 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0709587699984509, + "noiseRatio": 1.1937145569796526, + "lowerRatio": 0.860353591160936, + "upperRatio": 1.3331178004254396, + "candidate": [ + 1.825286999999662, + 1.4545190000026196, + 1.3061560000023746, + 1.358064000000013, + 1.3477190000012342, + 1.7332279999973252, + 1.6534879999999248 + ], + "baseline": [ + 1.441689999999653, + 1.3271779999995488, + 1.8665989999972226, + 1.3084890000027372, + 1.3080389999995532, + 1.34014599999864, + 1.374098000000231 + ], + "selfA": [ + 1.503101999998762, + 1.866879000001063, + 1.5306730000011157, + 1.3268359999965469, + 1.3114040000000386, + 1.2954500000014377, + 1.229760999998689 + ], + "selfB": [ + 1.5353610000020126, + 1.428369000001112, + 1.3675080000029993, + 1.263241000000562, + 1.2479779999994207, + 1.217211999999563, + 1.315429000002041 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9491387621241855, + "noiseRatio": 1.2281664964021166, + "lowerRatio": 0.613392415583522, + "upperRatio": 1.468659159910929, + "candidate": [ + 2.7835589999995136, + 1.2678280000000086, + 1.3855049999983748, + 1.2308220000013534, + 1.2134860000005574, + 1.4232220000012603, + 1.6662770000002638 + ], + "baseline": [ + 1.4958709999991697, + 1.26497400000153, + 1.5284000000028755, + 1.3558599999996659, + 1.339745999997831, + 1.2866360000007262, + 3.6923679999999877 + ], + "selfA": [ + 1.6474100000014005, + 1.4734779999998864, + 1.4286810000012338, + 1.2728050000005169, + 1.4605379999993602, + 1.2567310000013094, + 1.393856999999116 + ], + "selfB": [ + 1.2881079999970098, + 1.5525569999990694, + 1.2827010000000882, + 1.2742380000017874, + 1.4243639999986044, + 1.3787149999989197, + 1.9565249999977823 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 1.503101999998762, + 1.866879000001063, + 1.5306730000011157, + 1.3268359999965469, + 1.3114040000000386, + 1.2954500000014377, + 1.229760999998689 + ], + "baselineSelfB": [ + 1.5353610000020126, + 1.428369000001112, + 1.3675080000029993, + 1.263241000000562, + 1.2479779999994207, + 1.217211999999563, + 1.315429000002041 + ] + }, + "historicalReference": { + "A": 11, + "U": 6, + "C": 35, + "plateSwitches": 10, + "commands": [ + [ + 5, + -3 + ], + [ + 4, + 1 + ], + [ + 3, + 2 + ], + [ + 0, + 5 + ], + [ + 2, + -4 + ], + [ + 5, + -1 + ], + [ + 4, + 6 + ], + [ + 1, + -2 + ], + [ + 3, + 5 + ], + [ + 1, + -3 + ], + [ + 0, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "raven-tower-right-chest", + "title": "Raven Tower right chest", + "optimumA": 8, + "solvers": { + "originalBfs": { + "A": 8, + "U": 6, + "C": 23, + "plateSwitches": 7, + "commands": [ + [ + 1, + 5 + ], + [ + 2, + -1 + ], + [ + 5, + -5 + ], + [ + 0, + -5 + ], + [ + 2, + -2 + ], + [ + 3, + 2 + ], + [ + 4, + 1 + ], + [ + 5, + -2 + ] + ], + "samplesMs": [ + 36.35005600000295, + 36.217366999997466, + 35.26854900000035, + 37.717085000000225, + 38.20482799999809, + 36.07308000000194, + 37.628781999999774 + ], + "timingMs": { + "min": 35.26854900000035, + "median": 36.35005600000295, + "p95": 38.20482799999809, + "max": 38.20482799999809 + } + }, + "upstream": { + "A": 8, + "U": 6, + "C": 23, + "plateSwitches": 7, + "commands": [ + [ + 5, + -6 + ], + [ + 0, + -2 + ], + [ + 5, + -1 + ], + [ + 4, + 1 + ], + [ + 3, + 2 + ], + [ + 1, + 5 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 16.226090000000113, + 16.07094599999982, + 16.15751700000328, + 16.43437400000039, + 16.67321300000185, + 16.036153999997623, + 16.684089000002132 + ], + "timingMs": { + "min": 16.036153999997623, + "median": 16.226090000000113, + "p95": 16.684089000002132, + "max": 16.684089000002132 + } + }, + "candidate": { + "A": 8, + "U": 6, + "C": 23, + "plateSwitches": 7, + "commands": [ + [ + 1, + 5 + ], + [ + 4, + 1 + ], + [ + 5, + -6 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -2 + ], + [ + 3, + 2 + ], + [ + 5, + -1 + ] + ], + "samplesMs": [ + 0.13393200000064098, + 0.15595500000199536, + 0.12147299999924144, + 0.13800799999808078, + 0.13398199999937788, + 0.13488299999880837, + 0.13035599999784608 + ], + "timingMs": { + "min": 0.12147299999924144, + "median": 0.13398199999937788, + "p95": 0.15595500000199536, + "max": 0.15595500000199536 + } + }, + "baseline": { + "A": 8, + "U": 6, + "C": 23, + "plateSwitches": 7, + "commands": [ + [ + 1, + 5 + ], + [ + 4, + 1 + ], + [ + 5, + -6 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -2 + ], + [ + 3, + 2 + ], + [ + 5, + -1 + ] + ], + "samplesMs": [ + 0.14509800000087125, + 0.12792199999967124, + 0.12039100000038161, + 0.12083200000051875, + 0.24475800000072923, + 0.12461700000130804, + 0.11881899999934831 + ], + "timingMs": { + "min": 0.11881899999934831, + "median": 0.12461700000130804, + "p95": 0.24475800000072923, + "max": 0.24475800000072923 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.9758943552814838, + "noiseRatio": 1.7764179621697334, + "lowerRatio": 0.7351910747245831, + "upperRatio": 1.2954044539061347 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9758943552814838, + "noiseRatio": 1.7764179621697334, + "lowerRatio": 0.7351910747245831, + "upperRatio": 1.2954044539061347, + "candidate": [ + 0.13393200000064098, + 0.15595500000199536, + 0.12147299999924144, + 0.13800799999808078, + 0.13398199999937788, + 0.13488299999880837, + 0.13035599999784608 + ], + "baseline": [ + 0.14509800000087125, + 0.12792199999967124, + 0.12039100000038161, + 0.12083200000051875, + 0.24475800000072923, + 0.12461700000130804, + 0.11881899999934831 + ], + "selfA": [ + 0.07413100000121631, + 0.07765700000163633, + 0.07877899999948568, + 0.07014500000150292, + 0.07724599999710335, + 0.06782300000122632, + 0.06463699999949313 + ], + "selfB": [ + 0.14591000000291388, + 0.08611000000018976, + 0.08727100000032806, + 0.07589399999778834, + 0.1568760000009206, + 0.07953999999881489, + 0.06701100000282167 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.8266910608114032, + "noiseRatio": 1.3734118218671454, + "lowerRatio": 0.29445294009737427, + "upperRatio": 2.320975670337949, + "candidate": [ + 0.0999100000008184, + 0.09514299999864306, + 0.08793199999854551, + 0.09991099999751896, + 0.11775699999998324, + 0.12473799999861512, + 0.08612999999968451 + ], + "baseline": [ + 0.09277900000233785, + 0.07643499999903725, + 0.9708470000005036, + 0.08841299999767216, + 0.09959000000162632, + 0.0801610000016808, + 0.08239400000093156 + ], + "selfA": [ + 0.08329599999706261, + 0.12485799999922165, + 0.08619900000121561, + 0.0703060000014375, + 0.11444300000221119, + 0.07681600000069011, + 0.07889799999975367 + ], + "selfB": [ + 0.07929900000090129, + 0.0971559999998135, + 0.07422199999928125, + 0.10997599999973318, + 0.08546899999782909, + 0.06767200000103912, + 0.07990000000063446 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.07413100000121631, + 0.07765700000163633, + 0.07877899999948568, + 0.07014500000150292, + 0.07724599999710335, + 0.06782300000122632, + 0.06463699999949313 + ], + "baselineSelfB": [ + 0.14591000000291388, + 0.08611000000018976, + 0.08727100000032806, + 0.07589399999778834, + 0.1568760000009206, + 0.07953999999881489, + 0.06701100000282167 + ] + }, + "historicalReference": { + "A": 8, + "U": 6, + "C": 23, + "plateSwitches": 7, + "commands": [ + [ + 5, + -6 + ], + [ + 0, + -2 + ], + [ + 5, + -1 + ], + [ + 4, + 1 + ], + [ + 3, + 2 + ], + [ + 1, + 5 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "raven-tower-floor2-stairs-chest", + "title": "Chest by stairs (Raven Tower floor 2)", + "optimumA": 16, + "solvers": { + "originalBfs": { + "A": 16, + "U": 6, + "C": 48, + "plateSwitches": 15, + "commands": [ + [ + 0, + -1 + ], + [ + 4, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + 4 + ], + [ + 3, + -2 + ], + [ + 4, + -3 + ], + [ + 1, + 3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + -3 + ], + [ + 2, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -6 + ], + [ + 1, + 3 + ], + [ + 3, + -3 + ], + [ + 5, + 3 + ] + ], + "samplesMs": [ + 108.31925200000114, + 100.84043599999859, + 104.50316100000055, + 107.42984599999909, + 100.70825699999841, + 101.80743399999847, + 103.03595800000039 + ], + "timingMs": { + "min": 100.70825699999841, + "median": 103.03595800000039, + "p95": 108.31925200000114, + "max": 108.31925200000114 + } + }, + "upstream": { + "A": 16, + "U": 6, + "C": 48, + "plateSwitches": 15, + "commands": [ + [ + 5, + 3 + ], + [ + 3, + -1 + ], + [ + 4, + -1 + ], + [ + 3, + -1 + ], + [ + 0, + -2 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 2, + 4 + ], + [ + 1, + 6 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 2, + 6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 57.842703999998776, + 52.539254999999685, + 62.86090399999739, + 62.943789000000834, + 63.994024000003265, + 58.06553100000019, + 69.60019099999772 + ], + "timingMs": { + "min": 52.539254999999685, + "median": 62.86090399999739, + "p95": 69.60019099999772, + "max": 69.60019099999772 + } + }, + "candidate": { + "A": 16, + "U": 6, + "C": 48, + "plateSwitches": 15, + "commands": [ + [ + 5, + 3 + ], + [ + 0, + -1 + ], + [ + 4, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + 4 + ], + [ + 3, + -2 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 1, + 6 + ], + [ + 2, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 4.143740999999864, + 4.183720999997604, + 5.502520000001823, + 4.291022999997949, + 4.217161999997188, + 5.547338000000309, + 4.5542700000005425 + ], + "timingMs": { + "min": 4.143740999999864, + "median": 4.291022999997949, + "p95": 5.547338000000309, + "max": 5.547338000000309 + } + }, + "baseline": { + "A": 16, + "U": 6, + "C": 48, + "plateSwitches": 15, + "commands": [ + [ + 5, + 3 + ], + [ + 0, + -1 + ], + [ + 4, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + 4 + ], + [ + 3, + -2 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 1, + 6 + ], + [ + 2, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 4.331283999999869, + 4.332555000000866, + 5.416350000003149, + 4.885149000001547, + 4.654962000000523, + 5.4907620000012685, + 4.518545999999333 + ], + "timingMs": { + "min": 4.331283999999869, + "median": 4.654962000000523, + "p95": 5.4907620000012685, + "max": 5.4907620000012685 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.9616466713713308, + "noiseRatio": 1.1489469707722866, + "lowerRatio": 0.9058638580267406, + "upperRatio": 1.0208645729326158 + }, + "timingAttempts": [ + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.9616466713713308, + "noiseRatio": 1.1489469707722866, + "lowerRatio": 0.9058638580267406, + "upperRatio": 1.0208645729326158, + "candidate": [ + 4.143740999999864, + 4.183720999997604, + 5.502520000001823, + 4.291022999997949, + 4.217161999997188, + 5.547338000000309, + 4.5542700000005425 + ], + "baseline": [ + 4.331283999999869, + 4.332555000000866, + 5.416350000003149, + 4.885149000001547, + 4.654962000000523, + 5.4907620000012685, + 4.518545999999333 + ], + "selfA": [ + 4.625722000000678, + 5.576132000001962, + 4.508424999999988, + 5.803915000000416, + 6.617059000000154, + 4.632231999999931, + 4.604619000001549 + ], + "selfB": [ + 4.773423999999068, + 5.871366999999736, + 4.550218000000314, + 4.410737999998673, + 6.91250299999956, + 4.636637999999948, + 4.632652000000235 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 4.625722000000678, + 5.576132000001962, + 4.508424999999988, + 5.803915000000416, + 6.617059000000154, + 4.632231999999931, + 4.604619000001549 + ], + "baselineSelfB": [ + 4.773423999999068, + 5.871366999999736, + 4.550218000000314, + 4.410737999998673, + 6.91250299999956, + 4.636637999999948, + 4.632652000000235 + ] + }, + "historicalReference": { + "A": 16, + "U": 6, + "C": 48, + "plateSwitches": 15, + "commands": [ + [ + 5, + 3 + ], + [ + 3, + -1 + ], + [ + 4, + -1 + ], + [ + 3, + -1 + ], + [ + 0, + -2 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 2, + 4 + ], + [ + 1, + 6 + ], + [ + 4, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + -3 + ], + [ + 2, + 6 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "raven-tower-floor2-chest-2", + "title": "Second chest on Raven Tower floor 2", + "optimumA": 8, + "solvers": { + "originalBfs": { + "A": 8, + "U": 5, + "C": 29, + "plateSwitches": 7, + "commands": [ + [ + 3, + 1 + ], + [ + 2, + -5 + ], + [ + 4, + 5 + ], + [ + 0, + -1 + ], + [ + 3, + -3 + ], + [ + 1, + 5 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ] + ], + "samplesMs": [ + 10.414410000001226, + 10.942296000001079, + 11.73520599999756, + 12.065078999999969, + 11.859881000000314, + 10.259762000001501, + 10.593574999998964 + ], + "timingMs": { + "min": 10.259762000001501, + "median": 10.942296000001079, + "p95": 12.065078999999969, + "max": 12.065078999999969 + } + }, + "upstream": { + "A": 9, + "U": 5, + "C": 27, + "plateSwitches": 8, + "commands": [ + [ + 2, + -4 + ], + [ + 4, + 4 + ], + [ + 2, + -4 + ], + [ + 1, + 2 + ], + [ + 4, + 4 + ], + [ + 3, + -2 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 0, + -1 + ] + ], + "samplesMs": [ + 3.3507239999999, + 3.5150829999984126, + 3.589153000000806, + 3.159175000000687, + 3.235450000000128, + 3.046875, + 3.1596759999993083 + ], + "timingMs": { + "min": 3.046875, + "median": 3.235450000000128, + "p95": 3.589153000000806, + "max": 3.589153000000806 + } + }, + "candidate": { + "A": 8, + "U": 5, + "C": 29, + "plateSwitches": 7, + "commands": [ + [ + 3, + 1 + ], + [ + 2, + -5 + ], + [ + 4, + 5 + ], + [ + 3, + -3 + ], + [ + 0, + -1 + ], + [ + 1, + 5 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ] + ], + "samplesMs": [ + 0.2090749999988475, + 0.18787299999894458, + 0.19018599999981234, + 0.19074800000089454, + 0.26812300000165123, + 0.18184399999881862, + 0.18494799999825773 + ], + "timingMs": { + "min": 0.18184399999881862, + "median": 0.19018599999981234, + "p95": 0.26812300000165123, + "max": 0.26812300000165123 + } + }, + "baseline": { + "A": 8, + "U": 5, + "C": 29, + "plateSwitches": 7, + "commands": [ + [ + 3, + 1 + ], + [ + 2, + -5 + ], + [ + 4, + 5 + ], + [ + 3, + -3 + ], + [ + 0, + -1 + ], + [ + 1, + 5 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ] + ], + "samplesMs": [ + 0.1737619999985327, + 0.38508100000035483, + 0.1844790000031935, + 0.18815299999914714, + 0.3753949999991164, + 0.17896900000050664, + 0.2280639999989944 + ], + "timingMs": { + "min": 0.1737619999985327, + "median": 0.18815299999914714, + "p95": 0.38508100000035483, + "max": 0.38508100000035483 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.8645695885706429, + "noiseRatio": 2.560384072894661, + "lowerRatio": 0.6275363684867805, + "upperRatio": 1.1911350656594766 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.8645695885706429, + "noiseRatio": 2.560384072894661, + "lowerRatio": 0.6275363684867805, + "upperRatio": 1.1911350656594766, + "candidate": [ + 0.2090749999988475, + 0.18787299999894458, + 0.19018599999981234, + 0.19074800000089454, + 0.26812300000165123, + 0.18184399999881862, + 0.18494799999825773 + ], + "baseline": [ + 0.1737619999985327, + 0.38508100000035483, + 0.1844790000031935, + 0.18815299999914714, + 0.3753949999991164, + 0.17896900000050664, + 0.2280639999989944 + ], + "selfA": [ + 0.15834900000118068, + 0.30418900000222493, + 0.1636560000006284, + 0.1498460000002524, + 0.13784800000212272, + 0.14803300000130548, + 0.14752299999963725 + ], + "selfB": [ + 0.3392410000014934, + 0.161603999997169, + 0.141392999998061, + 0.1488050000007206, + 0.4518319999988307, + 0.23185000000012224, + 0.2425459999976738 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.1174300209462606, + "noiseRatio": 1.4011723583337032, + "lowerRatio": 0.6735988577571576, + "upperRatio": 1.8536994790482817, + "candidate": [ + 0.1515880000006291, + 0.16059099999984028, + 0.18262500000128057, + 0.22522899999967194, + 0.22564899999997579, + 0.1519380000027013, + 0.14048099999854458 + ], + "baseline": [ + 0.16291499999715597, + 0.39866999999867403, + 0.14552899999762303, + 0.14259400000082678, + 0.1534910000009404, + 0.10282500000175787, + 0.1042170000000624 + ], + "selfA": [ + 0.14237400000274647, + 0.18588900000031572, + 0.14339599999948405, + 0.16595000000233995, + 0.16012100000079954, + 0.13953000000037719, + 0.11175799999909941 + ], + "selfB": [ + 0.17087700000047334, + 0.14751199999955134, + 0.13004600000203936, + 0.13298000000213506, + 0.11249899999893387, + 0.10684000000037486, + 0.10226400000101421 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.15834900000118068, + 0.30418900000222493, + 0.1636560000006284, + 0.1498460000002524, + 0.13784800000212272, + 0.14803300000130548, + 0.14752299999963725 + ], + "baselineSelfB": [ + 0.3392410000014934, + 0.161603999997169, + 0.141392999998061, + 0.1488050000007206, + 0.4518319999988307, + 0.23185000000012224, + 0.2425459999976738 + ] + }, + "historicalReference": { + "A": 9, + "U": 5, + "C": 27, + "plateSwitches": 8, + "commands": [ + [ + 2, + -4 + ], + [ + 4, + 4 + ], + [ + 2, + -4 + ], + [ + 1, + 2 + ], + [ + 4, + 4 + ], + [ + 3, + -2 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 0, + -1 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "regression", + "deltas": { + "A": -1, + "U": 0, + "C": 2, + "plateSwitches": -1 + }, + "regressions": [ + "C" + ] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "raven-tower-floor3-entrance-chest", + "title": "Chest at entrance (Raven Tower floor 3)", + "optimumA": 14, + "solvers": { + "originalBfs": { + "A": 14, + "U": 5, + "C": 44, + "plateSwitches": 13, + "commands": [ + [ + 1, + 1 + ], + [ + 3, + -5 + ], + [ + 4, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + 2 + ], + [ + 3, + -2 + ], + [ + 4, + -4 + ], + [ + 0, + -2 + ], + [ + 4, + -2 + ], + [ + 2, + -4 + ], + [ + 1, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 8.83679100000154, + 8.982130000000325, + 8.625953000002482, + 8.88793699999951, + 8.592082000002847, + 8.99558000000252, + 9.141379000000597 + ], + "timingMs": { + "min": 8.592082000002847, + "median": 8.88793699999951, + "p95": 9.141379000000597, + "max": 9.141379000000597 + } + }, + "upstream": { + "A": 14, + "U": 5, + "C": 44, + "plateSwitches": 13, + "commands": [ + [ + 1, + 1 + ], + [ + 4, + -2 + ], + [ + 3, + -1 + ], + [ + 0, + -2 + ], + [ + 1, + 2 + ], + [ + 4, + -4 + ], + [ + 0, + -2 + ], + [ + 4, + -2 + ], + [ + 3, + -6 + ], + [ + 2, + -4 + ], + [ + 1, + 6 + ], + [ + 4, + -6 + ], + [ + 3, + -3 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 6.582016000000294, + 6.546243000000686, + 6.528865999996924, + 6.855569000003015, + 6.925384000001941, + 6.393413000001601, + 6.751021999996738 + ], + "timingMs": { + "min": 6.393413000001601, + "median": 6.582016000000294, + "p95": 6.925384000001941, + "max": 6.925384000001941 + } + }, + "candidate": { + "A": 14, + "U": 5, + "C": 44, + "plateSwitches": 13, + "commands": [ + [ + 3, + -4 + ], + [ + 1, + 1 + ], + [ + 4, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + 2 + ], + [ + 4, + -4 + ], + [ + 2, + -4 + ], + [ + 1, + 3 + ], + [ + 3, + -6 + ], + [ + 4, + -3 + ], + [ + 0, + -3 + ], + [ + 1, + 3 + ], + [ + 4, + -5 + ], + [ + 0, + -2 + ] + ], + "samplesMs": [ + 0.7942550000007031, + 0.608486000000994, + 0.6061120000013034, + 0.6011739999994461, + 1.504503999996814, + 0.582656999999017, + 0.59052900000097 + ], + "timingMs": { + "min": 0.582656999999017, + "median": 0.6061120000013034, + "p95": 1.504503999996814, + "max": 1.504503999996814 + } + }, + "baseline": { + "A": 14, + "U": 5, + "C": 44, + "plateSwitches": 13, + "commands": [ + [ + 3, + -4 + ], + [ + 1, + 1 + ], + [ + 4, + -2 + ], + [ + 0, + -2 + ], + [ + 1, + 2 + ], + [ + 4, + -4 + ], + [ + 2, + -4 + ], + [ + 1, + 3 + ], + [ + 3, + -6 + ], + [ + 4, + -3 + ], + [ + 0, + -3 + ], + [ + 1, + 3 + ], + [ + 4, + -5 + ], + [ + 0, + -2 + ] + ], + "samplesMs": [ + 0.8380610000021989, + 0.6058720000000903, + 0.5996830000003683, + 0.5866229999992356, + 0.6243800000011106, + 0.6162979999971867, + 0.6453909999981988 + ], + "timingMs": { + "min": 0.5866229999992356, + "median": 0.6162979999971867, + "p95": 0.8380610000021989, + "max": 0.8380610000021989 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", + "ratio": 1.1083765061419144, + "noiseRatio": 1.3801452889981793, + "lowerRatio": 0.7717859784158841, + "upperRatio": 1.591760557621026 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.1083765061419144, + "noiseRatio": 1.3801452889981793, + "lowerRatio": 0.7717859784158841, + "upperRatio": 1.591760557621026, + "candidate": [ + 0.7942550000007031, + 0.608486000000994, + 0.6061120000013034, + 0.6011739999994461, + 1.504503999996814, + 0.582656999999017, + 0.59052900000097 + ], + "baseline": [ + 0.8380610000021989, + 0.6058720000000903, + 0.5996830000003683, + 0.5866229999992356, + 0.6243800000011106, + 0.6162979999971867, + 0.6453909999981988 + ], + "selfA": [ + 0.5778299999983574, + 0.5204529999973602, + 0.5887160000020231, + 0.5333229999996547, + 0.5577990000019781, + 0.5679250000030152, + 0.890410999996675 + ], + "selfB": [ + 0.5575390000012703, + 0.5334729999995034, + 0.5357269999985874, + 0.8581520000007004, + 0.6916309999978694, + 0.6502689999979339, + 0.643779000001814 + ] + }, + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.8973039120558461, + "noiseRatio": 1.2384243053733441, + "lowerRatio": 0.7396232112067196, + "upperRatio": 1.0886006528609207, + "candidate": [ + 0.58195600000181, + 0.6624369999990449, + 0.559783000000607, + 0.5499369999997725, + 0.5522510000009788, + 0.575896999998804, + 0.5872239999989688 + ], + "baseline": [ + 0.7072239999979502, + 0.5310200000021723, + 0.7234690000004775, + 0.7314110000006622, + 0.579001000001881, + 0.7183319999967352, + 0.5725610000008601 + ], + "selfA": [ + 0.5859620000010182, + 0.4981999999981781, + 1.043922000000748, + 0.5956460000015795, + 0.5555260000000999, + 0.588064999999915, + 0.5505690000027244 + ], + "selfB": [ + 0.5465320000002976, + 0.693213000002288, + 0.8042210000021441, + 0.5980500000005122, + 0.5295069999992847, + 0.518289999999979, + 0.5515699999996286 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.5778299999983574, + 0.5204529999973602, + 0.5887160000020231, + 0.5333229999996547, + 0.5577990000019781, + 0.5679250000030152, + 0.890410999996675 + ], + "baselineSelfB": [ + 0.5575390000012703, + 0.5334729999995034, + 0.5357269999985874, + 0.8581520000007004, + 0.6916309999978694, + 0.6502689999979339, + 0.643779000001814 + ] + }, + "historicalReference": { + "A": 14, + "U": 5, + "C": 44, + "plateSwitches": 13, + "commands": [ + [ + 1, + 1 + ], + [ + 4, + -2 + ], + [ + 3, + -1 + ], + [ + 0, + -2 + ], + [ + 1, + 2 + ], + [ + 4, + -4 + ], + [ + 0, + -2 + ], + [ + 4, + -2 + ], + [ + 3, + -6 + ], + [ + 2, + -4 + ], + [ + 1, + 6 + ], + [ + 4, + -6 + ], + [ + 3, + -3 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "raven-tower-floor3-empty-chest", + "title": "Empty chest (Raven Tower floor 3)", + "optimumA": 9, + "solvers": { + "originalBfs": { + "A": 9, + "U": 5, + "C": 41, + "plateSwitches": 8, + "commands": [ + [ + 0, + -2 + ], + [ + 3, + 2 + ], + [ + 4, + 4 + ], + [ + 2, + 6 + ], + [ + 0, + -6 + ], + [ + 4, + 6 + ], + [ + 3, + -6 + ], + [ + 4, + 6 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 8.191428999998607, + 7.88301599999977, + 8.246202000002086, + 7.704927000002499, + 7.983926999997493, + 7.844818000001396, + 8.416607999999542 + ], + "timingMs": { + "min": 7.704927000002499, + "median": 7.983926999997493, + "p95": 8.416607999999542, + "max": 8.416607999999542 + } + }, + "upstream": { + "A": 9, + "U": 5, + "C": 37, + "plateSwitches": 8, + "commands": [ + [ + 4, + 6 + ], + [ + 2, + 2 + ], + [ + 0, + -4 + ], + [ + 2, + 4 + ], + [ + 4, + 6 + ], + [ + 0, + -4 + ], + [ + 3, + -4 + ], + [ + 4, + 4 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 5.718185999998241, + 5.81905800000095, + 5.573308000002726, + 5.475751000001765, + 5.505826999997225, + 6.900445999999647, + 6.195044000000053 + ], + "timingMs": { + "min": 5.475751000001765, + "median": 5.718185999998241, + "p95": 6.900445999999647, + "max": 6.900445999999647 + } + }, + "candidate": { + "A": 9, + "U": 5, + "C": 37, + "plateSwitches": 8, + "commands": [ + [ + 0, + -2 + ], + [ + 4, + 6 + ], + [ + 2, + 3 + ], + [ + 1, + -3 + ], + [ + 4, + 6 + ], + [ + 3, + -4 + ], + [ + 0, + -6 + ], + [ + 4, + 4 + ], + [ + 2, + 3 + ] + ], + "samplesMs": [ + 0.08624999999665306, + 0.08687099999951897, + 0.07469199999832199, + 0.07137700000021141, + 0.07719599999836646, + 0.1030350000000908, + 0.07694600000104401 + ], + "timingMs": { + "min": 0.07137700000021141, + "median": 0.07719599999836646, + "p95": 0.1030350000000908, + "max": 0.1030350000000908 + } + }, + "baseline": { + "A": 9, + "U": 5, + "C": 37, + "plateSwitches": 8, + "commands": [ + [ + 0, + -2 + ], + [ + 4, + 6 + ], + [ + 2, + 3 + ], + [ + 1, + -3 + ], + [ + 4, + 6 + ], + [ + 3, + -4 + ], + [ + 0, + -6 + ], + [ + 4, + 4 + ], + [ + 2, + 3 + ] + ], + "samplesMs": [ + 0.08483799999885377, + 0.07612399999925401, + 0.06979499999943073, + 0.06724199999734992, + 0.0774859999983164, + 0.15070700000069337, + 0.0695449999984703 + ], + "timingMs": { + "min": 0.06724199999734992, + "median": 0.07612399999925401, + "p95": 0.15070700000069337, + "max": 0.15070700000069337 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.9990243084827689, + "noiseRatio": 1.2588706040281896, + "lowerRatio": 0.8327126185582548, + "upperRatio": 1.1985522336235057 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9990243084827689, + "noiseRatio": 1.2588706040281896, + "lowerRatio": 0.8327126185582548, + "upperRatio": 1.1985522336235057, + "candidate": [ + 0.08624999999665306, + 0.08687099999951897, + 0.07469199999832199, + 0.07137700000021141, + 0.07719599999836646, + 0.1030350000000908, + 0.07694600000104401 + ], + "baseline": [ + 0.08483799999885377, + 0.07612399999925401, + 0.06979499999943073, + 0.06724199999734992, + 0.0774859999983164, + 0.15070700000069337, + 0.0695449999984703 + ], + "selfA": [ + 0.04280500000095344, + 0.03637499999967986, + 0.03615400000126101, + 0.0432650000002468, + 0.0386579999976675, + 0.03601400000115973, + 0.053790999998454936 + ], + "selfB": [ + 0.04246399999829009, + 0.037256000003253575, + 0.037186000001383945, + 0.03613500000210479, + 0.03869799999665702, + 0.03599400000166497, + 0.0370760000005248 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.205806775312928, + "noiseRatio": 1.7397850216411979, + "lowerRatio": 1.0386565433715693, + "upperRatio": 1.3998563708758325, + "candidate": [ + 0.06960400000025402, + 0.059549000001425156, + 0.05339000000094529, + 0.04511799999818322, + 0.04377599999861559, + 0.042392999999719905, + 0.044366999998601386 + ], + "baseline": [ + 0.05088700000123936, + 0.03899900000033085, + 0.04384600000048522, + 0.04312500000014552, + 0.040050999999948544, + 0.04046100000050501, + 0.036475000000791624 + ], + "selfA": [ + 0.10278499999913038, + 0.04206300000078045, + 0.04592899999988731, + 0.04229300000224612, + 0.03649500000028638, + 0.038207000001420965, + 0.06284400000004098 + ], + "selfB": [ + 0.045658999999432126, + 0.0559240000002319, + 0.06819200000245473, + 0.04268400000000838, + 0.03617400000075577, + 0.035823999998683576, + 0.03702599999814993 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.04280500000095344, + 0.03637499999967986, + 0.03615400000126101, + 0.0432650000002468, + 0.0386579999976675, + 0.03601400000115973, + 0.053790999998454936 + ], + "baselineSelfB": [ + 0.04246399999829009, + 0.037256000003253575, + 0.037186000001383945, + 0.03613500000210479, + 0.03869799999665702, + 0.03599400000166497, + 0.0370760000005248 + ] + }, + "historicalReference": { + "A": 9, + "U": 5, + "C": 37, + "plateSwitches": 8, + "commands": [ + [ + 4, + 6 + ], + [ + 2, + 2 + ], + [ + 0, + -4 + ], + [ + 2, + 4 + ], + [ + 4, + 6 + ], + [ + 0, + -4 + ], + [ + 3, + -4 + ], + [ + 4, + 4 + ], + [ + 1, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": -4, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "raven-tower-floor4-elixirs-chest", + "title": "Elixir chest (Raven Tower floor 4)", + "optimumA": 8, + "solvers": { + "originalBfs": { + "A": 8, + "U": 5, + "C": 36, + "plateSwitches": 7, + "commands": [ + [ + 1, + -5 + ], + [ + 2, + -5 + ], + [ + 1, + -5 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ], + [ + 2, + -6 + ], + [ + 1, + -3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 83.17834399999992, + 74.13739599999826, + 74.50477899999896, + 75.47908599999937, + 76.50029300000097, + 78.36638099999982, + 71.84072899999956 + ], + "timingMs": { + "min": 71.84072899999956, + "median": 75.47908599999937, + "p95": 83.17834399999992, + "max": 83.17834399999992 + } + }, + "upstream": { + "A": 8, + "U": 5, + "C": 36, + "plateSwitches": 7, + "commands": [ + [ + 1, + -4 + ], + [ + 2, + -5 + ], + [ + 4, + 6 + ], + [ + 1, + -6 + ], + [ + 2, + -6 + ], + [ + 3, + 3 + ], + [ + 1, + -3 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 110.47301000000152, + 114.14526299999852, + 110.3698000000004, + 114.36231000000043, + 111.61005700000169, + 121.12372700000196, + 109.82555099999809 + ], + "timingMs": { + "min": 109.82555099999809, + "median": 111.61005700000169, + "p95": 121.12372700000196, + "max": 121.12372700000196 + } + }, + "candidate": { + "A": 8, + "U": 5, + "C": 36, + "plateSwitches": 7, + "commands": [ + [ + 1, + -5 + ], + [ + 2, + -5 + ], + [ + 1, + -5 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ], + [ + 2, + -6 + ], + [ + 1, + -3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.10121200000139652, + 0.09749600000213832, + 0.09930900000108522, + 0.1320479999994859, + 0.1046169999972335, + 0.10208399999828544, + 0.1787189999995462 + ], + "timingMs": { + "min": 0.09749600000213832, + "median": 0.10208399999828544, + "p95": 0.1787189999995462, + "max": 0.1787189999995462 + } + }, + "baseline": { + "A": 8, + "U": 5, + "C": 36, + "plateSwitches": 7, + "commands": [ + [ + 1, + -5 + ], + [ + 2, + -5 + ], + [ + 1, + -5 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ], + [ + 2, + -6 + ], + [ + 1, + -3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.17902999999932945, + 0.1783289999984845, + 0.18321600000126637, + 0.20663100000092527, + 0.09565400000064983, + 0.09541299999909825, + 0.3788600000007136 + ], + "timingMs": { + "min": 0.09541299999909825, + "median": 0.17902999999932945, + "p95": 0.3788600000007136, + "max": 0.3788600000007136 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.6675911082749053, + "noiseRatio": 1.184396583079744, + "lowerRatio": 0.46656469874395085, + "upperRatio": 0.9552327663184453 + }, + "timingAttempts": [ + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.6675911082749053, + "noiseRatio": 1.184396583079744, + "lowerRatio": 0.46656469874395085, + "upperRatio": 0.9552327663184453, + "candidate": [ + 0.10121200000139652, + 0.09749600000213832, + 0.09930900000108522, + 0.1320479999994859, + 0.1046169999972335, + 0.10208399999828544, + 0.1787189999995462 + ], + "baseline": [ + 0.17902999999932945, + 0.1783289999984845, + 0.18321600000126637, + 0.20663100000092527, + 0.09565400000064983, + 0.09541299999909825, + 0.3788600000007136 + ], + "selfA": [ + 0.04361500000231899, + 0.06065099999977974, + 0.037575999998807674, + 0.03660500000114553, + 0.03620500000033644, + 0.03787699999884353, + 0.04167299999971874 + ], + "selfB": [ + 0.0431639999987965, + 0.04461700000319979, + 0.03866800000105286, + 0.03752600000007078, + 0.03514299999733339, + 0.0369460000001709, + 0.04143300000214367 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.04361500000231899, + 0.06065099999977974, + 0.037575999998807674, + 0.03660500000114553, + 0.03620500000033644, + 0.03787699999884353, + 0.04167299999971874 + ], + "baselineSelfB": [ + 0.0431639999987965, + 0.04461700000319979, + 0.03866800000105286, + 0.03752600000007078, + 0.03514299999733339, + 0.0369460000001709, + 0.04143300000214367 + ] + }, + "historicalReference": { + "A": 8, + "U": 5, + "C": 36, + "plateSwitches": 7, + "commands": [ + [ + 1, + -4 + ], + [ + 2, + -5 + ], + [ + 4, + 6 + ], + [ + 1, + -6 + ], + [ + 2, + -6 + ], + [ + 3, + 3 + ], + [ + 1, + -3 + ], + [ + 0, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "raven-tower-floor4-food-chest", + "title": "Food chest (Raven Tower floor 4)", + "optimumA": 11, + "solvers": { + "originalBfs": { + "A": 11, + "U": 6, + "C": 43, + "plateSwitches": 10, + "commands": [ + [ + 2, + 4 + ], + [ + 0, + 4 + ], + [ + 2, + 4 + ], + [ + 3, + -1 + ], + [ + 4, + 6 + ], + [ + 0, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + 2 + ], + [ + 5, + -3 + ] + ], + "samplesMs": [ + 97.7792389999995, + 91.33732400000008, + 92.23392299999978, + 91.68276400000104, + 102.99014899999747, + 95.99610599999869, + 99.89724299999943 + ], + "timingMs": { + "min": 91.33732400000008, + "median": 95.99610599999869, + "p95": 102.99014899999747, + "max": 102.99014899999747 + } + }, + "upstream": { + "A": 11, + "U": 6, + "C": 43, + "plateSwitches": 10, + "commands": [ + [ + 2, + 2 + ], + [ + 5, + -3 + ], + [ + 4, + 2 + ], + [ + 1, + 2 + ], + [ + 3, + -4 + ], + [ + 2, + 6 + ], + [ + 0, + 6 + ], + [ + 4, + 6 + ], + [ + 2, + 6 + ], + [ + 1, + 3 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 54.11020300000018, + 57.60958899999969, + 61.53917400000137, + 55.85695200000191, + 61.156958999999915, + 53.834318000001076, + 59.46566299999904 + ], + "timingMs": { + "min": 53.834318000001076, + "median": 57.60958899999969, + "p95": 61.53917400000137, + "max": 61.53917400000137 + } + }, + "candidate": { + "A": 11, + "U": 6, + "C": 43, + "plateSwitches": 10, + "commands": [ + [ + 2, + 4 + ], + [ + 0, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 6 + ], + [ + 0, + 5 + ], + [ + 3, + -1 + ], + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + 2 + ], + [ + 5, + -3 + ] + ], + "samplesMs": [ + 0.2506780000003346, + 0.2718989999993937, + 0.254493000000366, + 0.2518290000007255, + 0.25441200000204844, + 0.25262999999904423, + 0.25344099999711034 + ], + "timingMs": { + "min": 0.2506780000003346, + "median": 0.25344099999711034, + "p95": 0.2718989999993937, + "max": 0.2718989999993937 + } + }, + "baseline": { + "A": 11, + "U": 6, + "C": 43, + "plateSwitches": 10, + "commands": [ + [ + 2, + 4 + ], + [ + 0, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 6 + ], + [ + 0, + 5 + ], + [ + 3, + -1 + ], + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 3, + -3 + ], + [ + 4, + 2 + ], + [ + 5, + -3 + ] + ], + "samplesMs": [ + 0.253590999996959, + 0.2544530000013765, + 0.3543040000004112, + 0.3376579999967362, + 0.689728000001196, + 0.24406699999963166, + 0.2806319999981497 + ], + "timingMs": { + "min": 0.24406699999963166, + "median": 0.2806319999981497, + "p95": 0.689728000001196, + "max": 0.689728000001196 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.791794191123639, + "noiseRatio": 1.2867074792823932, + "lowerRatio": 0.5366177466775199, + "upperRatio": 1.1683140279628057 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.791794191123639, + "noiseRatio": 1.2867074792823932, + "lowerRatio": 0.5366177466775199, + "upperRatio": 1.1683140279628057, + "candidate": [ + 0.2506780000003346, + 0.2718989999993937, + 0.254493000000366, + 0.2518290000007255, + 0.25441200000204844, + 0.25262999999904423, + 0.25344099999711034 + ], + "baseline": [ + 0.253590999996959, + 0.2544530000013765, + 0.3543040000004112, + 0.3376579999967362, + 0.689728000001196, + 0.24406699999963166, + 0.2806319999981497 + ], + "selfA": [ + 0.2361849999979313, + 0.24300600000060513, + 0.20763200000146753, + 0.15654599999834318, + 0.17571500000121887, + 0.19390200000270852, + 0.18893499999830965 + ], + "selfB": [ + 0.21192900000096415, + 0.2919690000016999, + 0.1893549999986135, + 0.16495800000120653, + 0.18872399999963818, + 0.28813399999853573, + 0.16666000000259373 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.2835505202555417, + "noiseRatio": 1.1576822667634536, + "lowerRatio": 0.609052941215962, + "upperRatio": 2.7050225465770965, + "candidate": [ + 0.20972600000095554, + 0.2006519999995362, + 0.20491899999979069, + 0.190335999999661, + 1.4210389999971085, + 0.21012599999812664, + 0.205749000000651 + ], + "baseline": [ + 0.2024440000022878, + 0.24982600000294042, + 0.1870710000002873, + 0.21022699999957695, + 0.2277429999994638, + 0.17295000000012806, + 0.22424799999862444 + ], + "selfA": [ + 0.18246500000168453, + 0.16624100000262843, + 0.20588900000075228, + 0.16185400000176742, + 0.2058599999982107, + 0.1571670000012091, + 0.19158900000184076 + ], + "selfB": [ + 0.20850399999835645, + 0.1572569999989355, + 0.1819839999989199, + 0.18515900000056718, + 0.2207219999982044, + 0.15332100000159699, + 0.15602399999988847 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.2361849999979313, + 0.24300600000060513, + 0.20763200000146753, + 0.15654599999834318, + 0.17571500000121887, + 0.19390200000270852, + 0.18893499999830965 + ], + "baselineSelfB": [ + 0.21192900000096415, + 0.2919690000016999, + 0.1893549999986135, + 0.16495800000120653, + 0.18872399999963818, + 0.28813399999853573, + 0.16666000000259373 + ] + }, + "historicalReference": { + "A": 11, + "U": 6, + "C": 43, + "plateSwitches": 10, + "commands": [ + [ + 2, + 2 + ], + [ + 5, + -3 + ], + [ + 4, + 2 + ], + [ + 1, + 2 + ], + [ + 3, + -4 + ], + [ + 2, + 6 + ], + [ + 0, + 6 + ], + [ + 4, + 6 + ], + [ + 2, + 6 + ], + [ + 1, + 3 + ], + [ + 0, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "raven-tower-top-chest", + "title": "Chest at the top of Raven Tower", + "optimumA": 20, + "solvers": { + "originalBfs": { + "A": 20, + "U": 6, + "C": 48, + "plateSwitches": 19, + "commands": [ + [ + 1, + -3 + ], + [ + 3, + 1 + ], + [ + 0, + -1 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + -1 + ], + [ + 4, + -2 + ], + [ + 3, + 2 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + -6 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 5, + -4 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 92.66402200000084, + 85.79478199999721, + 82.07782800000132, + 83.04535400000168, + 84.04145399999834, + 79.77004399999714, + 83.32641700000022 + ], + "timingMs": { + "min": 79.77004399999714, + "median": 83.32641700000022, + "p95": 92.66402200000084, + "max": 92.66402200000084 + } + }, + "upstream": { + "A": 20, + "U": 6, + "C": 48, + "plateSwitches": 19, + "commands": [ + [ + 3, + 1 + ], + [ + 0, + -1 + ], + [ + 4, + -2 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 3, + 1 + ], + [ + 0, + -1 + ], + [ + 3, + 1 + ], + [ + 5, + -4 + ], + [ + 2, + -2 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + -6 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 46.34331300000122, + 50.413792999999714, + 48.378396000000066, + 45.21305199999915, + 47.07778799999869, + 45.144538000000466, + 45.60020400000212 + ], + "timingMs": { + "min": 45.144538000000466, + "median": 46.34331300000122, + "p95": 50.413792999999714, + "max": 50.413792999999714 + } + }, + "candidate": { + "A": 20, + "U": 6, + "C": 48, + "plateSwitches": 19, + "commands": [ + [ + 1, + -3 + ], + [ + 3, + 1 + ], + [ + 0, + -1 + ], + [ + 4, + -2 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 3, + 2 + ], + [ + 5, + -4 + ], + [ + 2, + -1 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + -6 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 8.81014999999752, + 9.386878000001161, + 7.199506000000838, + 7.277092000000266, + 8.341165000001638, + 7.16088800000216, + 7.3677390000011656 + ], + "timingMs": { + "min": 7.16088800000216, + "median": 7.3677390000011656, + "p95": 9.386878000001161, + "max": 9.386878000001161 + } + }, + "baseline": { + "A": 20, + "U": 6, + "C": 48, + "plateSwitches": 19, + "commands": [ + [ + 1, + -3 + ], + [ + 3, + 1 + ], + [ + 0, + -1 + ], + [ + 4, + -2 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 3, + 2 + ], + [ + 5, + -4 + ], + [ + 2, + -1 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + -6 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 7.32115899999917, + 7.708662999997614, + 7.653169000001071, + 7.451024999998481, + 7.424524999998539, + 7.58204200000182, + 7.2477880000005825 + ], + "timingMs": { + "min": 7.2477880000005825, + "median": 7.451024999998481, + "p95": 7.708662999997614, + "max": 7.708662999997614 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", + "ratio": 1.054739439857676, + "noiseRatio": 1.1587383305418397, + "lowerRatio": 0.9383082947744821, + "upperRatio": 1.1856180875590174 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.054739439857676, + "noiseRatio": 1.1587383305418397, + "lowerRatio": 0.9383082947744821, + "upperRatio": 1.1856180875590174, + "candidate": [ + 8.81014999999752, + 9.386878000001161, + 7.199506000000838, + 7.277092000000266, + 8.341165000001638, + 7.16088800000216, + 7.3677390000011656 + ], + "baseline": [ + 7.32115899999917, + 7.708662999997614, + 7.653169000001071, + 7.451024999998481, + 7.424524999998539, + 7.58204200000182, + 7.2477880000005825 + ], + "selfA": [ + 10.304070000001957, + 7.409732000000076, + 7.386548000002222, + 7.220076999998128, + 7.574179999999615, + 7.289161000000604, + 7.3406479999976 + ], + "selfB": [ + 7.7767449999992095, + 7.701472000000649, + 7.5592670000005455, + 7.3004779999973834, + 7.607731000000058, + 7.475791000000754, + 7.181249000001117 + ] + }, + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.9182661984217206, + "noiseRatio": 1.1770345018327855, + "lowerRatio": 0.8390194121206238, + "upperRatio": 1.004997976188246, + "candidate": [ + 6.679714000001695, + 6.669749000000593, + 6.973034999999072, + 7.089050000002317, + 6.658572000000277, + 6.836018999998487, + 7.377133000001777 + ], + "baseline": [ + 8.675147000001743, + 7.396783000000141, + 7.555703000001813, + 7.18513399999938, + 7.0869779999993625, + 7.374659000000975, + 7.377963999999338 + ], + "selfA": [ + 7.881371999999828, + 7.177472999999736, + 7.4805989999986195, + 7.079416000000492, + 7.387920000001031, + 7.338244999999006, + 9.866481000000931 + ], + "selfB": [ + 7.339386999999988, + 7.687040000000707, + 7.126546999999846, + 7.171593999999459, + 7.120296999997663, + 7.5404189999972004, + 7.502402000001894 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 10.304070000001957, + 7.409732000000076, + 7.386548000002222, + 7.220076999998128, + 7.574179999999615, + 7.289161000000604, + 7.3406479999976 + ], + "baselineSelfB": [ + 7.7767449999992095, + 7.701472000000649, + 7.5592670000005455, + 7.3004779999973834, + 7.607731000000058, + 7.475791000000754, + 7.181249000001117 + ] + }, + "historicalReference": { + "A": 20, + "U": 6, + "C": 48, + "plateSwitches": 19, + "commands": [ + [ + 3, + 1 + ], + [ + 0, + -1 + ], + [ + 4, + -2 + ], + [ + 2, + -1 + ], + [ + 0, + -1 + ], + [ + 3, + 1 + ], + [ + 0, + -1 + ], + [ + 3, + 1 + ], + [ + 5, + -4 + ], + [ + 2, + -2 + ], + [ + 0, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 4, + -6 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "fire-lizard-cave-chest", + "title": "Fire lizard cave box", + "optimumA": 10, + "solvers": { + "originalBfs": { + "A": 10, + "U": 6, + "C": 41, + "plateSwitches": 9, + "commands": [ + [ + 0, + -3 + ], + [ + 2, + -5 + ], + [ + 4, + 4 + ], + [ + 1, + 3 + ], + [ + 5, + -5 + ], + [ + 0, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + 6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 65.79064300000027, + 68.84199600000284, + 77.1776269999973, + 66.0915359999999, + 66.05964799999856, + 67.79507999999987, + 67.62880000000223 + ], + "timingMs": { + "min": 65.79064300000027, + "median": 67.62880000000223, + "p95": 77.1776269999973, + "max": 77.1776269999973 + } + }, + "upstream": { + "A": 10, + "U": 6, + "C": 41, + "plateSwitches": 9, + "commands": [ + [ + 0, + -3 + ], + [ + 2, + -5 + ], + [ + 5, + -5 + ], + [ + 1, + 3 + ], + [ + 4, + 4 + ], + [ + 0, + -6 + ], + [ + 4, + 6 + ], + [ + 3, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 54.80736099999922, + 57.56618399999934, + 52.73060600000099, + 58.65374099999826, + 56.06977199999892, + 57.91260599999805, + 51.749800000001414 + ], + "timingMs": { + "min": 51.749800000001414, + "median": 56.06977199999892, + "p95": 58.65374099999826, + "max": 58.65374099999826 + } + }, + "candidate": { + "A": 10, + "U": 6, + "C": 41, + "plateSwitches": 9, + "commands": [ + [ + 0, + -3 + ], + [ + 2, + -5 + ], + [ + 4, + 4 + ], + [ + 1, + 3 + ], + [ + 5, + -5 + ], + [ + 0, + -6 + ], + [ + 3, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + 6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 0.15909000000101514, + 0.154592999999295, + 0.23011599999881582, + 0.21219900000141934, + 0.16016099999978906, + 0.165168999999878, + 0.15812799999912386 + ], + "timingMs": { + "min": 0.154592999999295, + "median": 0.16016099999978906, + "p95": 0.23011599999881582, + "max": 0.23011599999881582 + } + }, + "baseline": { + "A": 10, + "U": 6, + "C": 41, + "plateSwitches": 9, + "commands": [ + [ + 0, + -3 + ], + [ + 2, + -5 + ], + [ + 4, + 4 + ], + [ + 1, + 3 + ], + [ + 5, + -5 + ], + [ + 0, + -6 + ], + [ + 3, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + 6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 0.2369170000019949, + 0.14187399999718764, + 0.1439460000001418, + 0.8497690000003786, + 0.2489949999981036, + 0.1520989999989979, + 0.14818300000115414 + ], + "timingMs": { + "min": 0.14187399999718764, + "median": 0.1520989999989979, + "p95": 0.8497690000003786, + "max": 0.8497690000003786 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.804293537589002, + "noiseRatio": 1.3254050811839404, + "lowerRatio": 0.4270201739792324, + "upperRatio": 1.5148888367014062 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.804293537589002, + "noiseRatio": 1.3254050811839404, + "lowerRatio": 0.4270201739792324, + "upperRatio": 1.5148888367014062, + "candidate": [ + 0.15909000000101514, + 0.154592999999295, + 0.23011599999881582, + 0.21219900000141934, + 0.16016099999978906, + 0.165168999999878, + 0.15812799999912386 + ], + "baseline": [ + 0.2369170000019949, + 0.14187399999718764, + 0.1439460000001418, + 0.8497690000003786, + 0.2489949999981036, + 0.1520989999989979, + 0.14818300000115414 + ], + "selfA": [ + 0.08253400000103284, + 0.07786699999996927, + 0.07955999999830965, + 0.0776369999985036, + 0.07868900000175927, + 0.07694500000070548, + 0.07762600000205566 + ], + "selfB": [ + 0.0803410000007716, + 0.07866899999862653, + 0.1316079999996873, + 0.08522799999991548, + 0.0790290000004461, + 0.07833799999934854, + 0.07584400000268943 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.141551086004325, + "noiseRatio": 1.286651420752876, + "lowerRatio": 0.9099141786903442, + "upperRatio": 1.4321558147750646, + "candidate": [ + 0.09527300000263494, + 0.11260899999979301, + 0.2097460000004503, + 0.17250999999669148, + 0.3272929999984626, + 0.17456200000015087, + 0.2067719999977271 + ], + "baseline": [ + 0.1129800000016985, + 0.08226300000023912, + 0.17137799999909475, + 0.17301100000258884, + 0.21042700000180048, + 0.18348599999808357, + 0.17061699999976554 + ], + "selfA": [ + 0.10280499999862514, + 0.07885899999746471, + 0.24328700000114623, + 0.1657100000011269, + 0.1849079999992682, + 0.16883400000006077, + 0.17228999999861117 + ], + "selfB": [ + 0.09511299999940093, + 0.09594500000093831, + 0.16367600000012317, + 0.1932410000008531, + 0.18885399999999208, + 0.14862400000129128, + 0.15884899999946356 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.08253400000103284, + 0.07786699999996927, + 0.07955999999830965, + 0.0776369999985036, + 0.07868900000175927, + 0.07694500000070548, + 0.07762600000205566 + ], + "baselineSelfB": [ + 0.0803410000007716, + 0.07866899999862653, + 0.1316079999996873, + 0.08522799999991548, + 0.0790290000004461, + 0.07833799999934854, + 0.07584400000268943 + ] + }, + "historicalReference": { + "A": 10, + "U": 6, + "C": 41, + "plateSwitches": 9, + "commands": [ + [ + 0, + -3 + ], + [ + 2, + -5 + ], + [ + 5, + -5 + ], + [ + 1, + 3 + ], + [ + 4, + 4 + ], + [ + 0, + -6 + ], + [ + 4, + 6 + ], + [ + 3, + -3 + ], + [ + 2, + -3 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "silas-tavern-basement-chest", + "title": "Silas tavern basement chest", + "optimumA": 9, + "solvers": { + "originalBfs": { + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + -2 + ], + [ + 3, + -4 + ], + [ + 4, + 6 + ], + [ + 2, + 5 + ], + [ + 3, + -5 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ] + ], + "samplesMs": [ + 7.5152410000009695, + 6.635957999998936, + 7.778768000000127, + 6.651370999999926, + 6.911211999999068, + 6.966225000000122, + 6.363377000001492 + ], + "timingMs": { + "min": 6.363377000001492, + "median": 6.911211999999068, + "p95": 7.778768000000127, + "max": 7.778768000000127 + } + }, + "upstream": { + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 4, + 2 + ], + [ + 0, + 2 + ], + [ + 2, + 4 + ], + [ + 1, + -1 + ], + [ + 3, + -6 + ], + [ + 4, + 6 + ], + [ + 2, + 3 + ], + [ + 3, + -3 + ], + [ + 1, + -3 + ] + ], + "samplesMs": [ + 4.033581000003323, + 4.462597000001551, + 4.053450999999768, + 3.9988890000022366, + 4.383406999997533, + 4.350818999999319, + 4.036565999998857 + ], + "timingMs": { + "min": 3.9988890000022366, + "median": 4.053450999999768, + "p95": 4.462597000001551, + "max": 4.462597000001551 + } + }, + "candidate": { + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 4 + ], + [ + 3, + -3 + ], + [ + 1, + -4 + ], + [ + 3, + -3 + ], + [ + 4, + 6 + ], + [ + 2, + 3 + ], + [ + 3, + -3 + ] + ], + "samplesMs": [ + 0.09969999999884749, + 0.09260899999935646, + 0.23263000000224565, + 0.09626500000013039, + 0.19647600000098464, + 0.12190299999929266, + 0.09030600000187405 + ], + "timingMs": { + "min": 0.09030600000187405, + "median": 0.09969999999884749, + "p95": 0.23263000000224565, + "max": 0.23263000000224565 + } + }, + "baseline": { + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 0, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 4 + ], + [ + 3, + -3 + ], + [ + 1, + -4 + ], + [ + 3, + -3 + ], + [ + 4, + 6 + ], + [ + 2, + 3 + ], + [ + 3, + -3 + ] + ], + "samplesMs": [ + 0.09748599999875296, + 0.08798300000125892, + 0.0884330000008049, + 0.10627999999996973, + 0.10178399999858811, + 0.08879399999932502, + 0.08768199999758508 + ], + "timingMs": { + "min": 0.08768199999758508, + "median": 0.08879399999932502, + "p95": 0.10627999999996973, + "max": 0.10627999999996973 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.320488556379239, + "noiseRatio": 1.180032118378632, + "lowerRatio": 0.8714691763276781, + "upperRatio": 2.0008625375326963 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.320488556379239, + "noiseRatio": 1.180032118378632, + "lowerRatio": 0.8714691763276781, + "upperRatio": 2.0008625375326963, + "candidate": [ + 0.09969999999884749, + 0.09260899999935646, + 0.23263000000224565, + 0.09626500000013039, + 0.19647600000098464, + 0.12190299999929266, + 0.09030600000187405 + ], + "baseline": [ + 0.09748599999875296, + 0.08798300000125892, + 0.0884330000008049, + 0.10627999999996973, + 0.10178399999858811, + 0.08879399999932502, + 0.08768199999758508 + ], + "selfA": [ + 0.10103200000230572, + 0.09945000000152504, + 0.09373100000084378, + 0.09153800000058254, + 0.10363599999982398, + 0.09454199999890989, + 0.09767700000156765 + ], + "selfB": [ + 0.14004100000238395, + 0.10149299999829964, + 0.08953499999915948, + 0.09194800000113901, + 0.0951630000017758, + 0.09010499999931199, + 0.09333100000003469 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.173680563745284, + "noiseRatio": 1.3486853435922972, + "lowerRatio": 0.9446854795201814, + "upperRatio": 1.4581848621332805, + "candidate": [ + 0.06841300000087358, + 0.07091600000057952, + 0.06845400000020163, + 0.0600799999992887, + 0.05476199999975506, + 0.08401700000104029, + 0.07028599999830476 + ], + "baseline": [ + 0.07296900000073947, + 0.05095599999913247, + 0.0671409999995376, + 0.05230899999878602, + 0.050645999999687774, + 0.04899400000067544, + 0.06490699999994831 + ], + "selfA": [ + 0.0682730000007723, + 0.048923000002105255, + 0.05582399999912013, + 0.05029599999761558, + 0.04884400000082678, + 0.05027499999778229, + 0.11249899999893387 + ], + "selfB": [ + 0.0546119999999064, + 0.04967399999804911, + 0.05346099999951548, + 0.05699600000298233, + 0.049043999999412335, + 0.056735999998636544, + 0.06904399999984889 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.10103200000230572, + 0.09945000000152504, + 0.09373100000084378, + 0.09153800000058254, + 0.10363599999982398, + 0.09454199999890989, + 0.09767700000156765 + ], + "baselineSelfB": [ + 0.14004100000238395, + 0.10149299999829964, + 0.08953499999915948, + 0.09194800000113901, + 0.0951630000017758, + 0.09010499999931199, + 0.09333100000003469 + ] + }, + "historicalReference": { + "A": 9, + "U": 5, + "C": 30, + "plateSwitches": 8, + "commands": [ + [ + 4, + 2 + ], + [ + 0, + 2 + ], + [ + 2, + 4 + ], + [ + 1, + -1 + ], + [ + 3, + -6 + ], + [ + 4, + 6 + ], + [ + 2, + 3 + ], + [ + 3, + -3 + ], + [ + 1, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "silas-tavern-storeroom-chest", + "title": "Tavern storeroom chest", + "optimumA": 9, + "solvers": { + "originalBfs": { + "A": 9, + "U": 6, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + -3 + ], + [ + 3, + 4 + ], + [ + 5, + -6 + ], + [ + 0, + 2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -6 + ], + [ + 3, + 3 + ], + [ + 5, + -3 + ] + ], + "samplesMs": [ + 86.35269200000039, + 86.39734999999928, + 87.22469300000012, + 86.90162799999962, + 92.51974999999948, + 93.56100699999661, + 89.50045800000225 + ], + "timingMs": { + "min": 86.35269200000039, + "median": 87.22469300000012, + "p95": 93.56100699999661, + "max": 93.56100699999661 + } + }, + "upstream": { + "A": 9, + "U": 6, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + -3 + ], + [ + 5, + -6 + ], + [ + 3, + 4 + ], + [ + 2, + -6 + ], + [ + 4, + 2 + ], + [ + 1, + -2 + ], + [ + 5, + -3 + ], + [ + 3, + 3 + ], + [ + 0, + 2 + ] + ], + "samplesMs": [ + 30.261741000002075, + 30.76007000000027, + 31.442306000000826, + 30.83804699999746, + 30.93786800000089, + 31.911792999999307, + 30.919159999997646 + ], + "timingMs": { + "min": 30.261741000002075, + "median": 30.919159999997646, + "p95": 31.911792999999307, + "max": 31.911792999999307 + } + }, + "candidate": { + "A": 9, + "U": 6, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + -3 + ], + [ + 3, + 4 + ], + [ + 5, + -6 + ], + [ + 0, + 2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -6 + ], + [ + 3, + 3 + ], + [ + 5, + -3 + ] + ], + "samplesMs": [ + 0.07831699999951525, + 0.08786300000065239, + 0.08873400000084075, + 0.08549800000037067, + 0.07897899999807123, + 0.15704600000026403, + 0.08667099999729544 + ], + "timingMs": { + "min": 0.07831699999951525, + "median": 0.08667099999729544, + "p95": 0.15704600000026403, + "max": 0.15704600000026403 + } + }, + "baseline": { + "A": 9, + "U": 6, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + -3 + ], + [ + 3, + 4 + ], + [ + 5, + -6 + ], + [ + 0, + 2 + ], + [ + 1, + -2 + ], + [ + 4, + 2 + ], + [ + 2, + -6 + ], + [ + 3, + 3 + ], + [ + 5, + -3 + ] + ], + "samplesMs": [ + 0.0747029999984079, + 0.10290400000303634, + 0.09423199999946519, + 0.07635500000105822, + 0.07498299999861047, + 0.07961000000068452, + 0.09744700000010198 + ], + "timingMs": { + "min": 0.0747029999984079, + "median": 0.07961000000068452, + "p95": 0.10290400000303634, + "max": 0.10290400000303634 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.0827207798614293, + "noiseRatio": 1.2755939756219734, + "lowerRatio": 0.8053439182441593, + "upperRatio": 1.455631886684635 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0827207798614293, + "noiseRatio": 1.2755939756219734, + "lowerRatio": 0.8053439182441593, + "upperRatio": 1.455631886684635, + "candidate": [ + 0.07831699999951525, + 0.08786300000065239, + 0.08873400000084075, + 0.08549800000037067, + 0.07897899999807123, + 0.15704600000026403, + 0.08667099999729544 + ], + "baseline": [ + 0.0747029999984079, + 0.10290400000303634, + 0.09423199999946519, + 0.07635500000105822, + 0.07498299999861047, + 0.07961000000068452, + 0.09744700000010198 + ], + "selfA": [ + 0.06413600000087172, + 0.035683999998582294, + 0.03358999999909429, + 0.040950999999040505, + 0.03813799999988987, + 0.03569399999832967, + 0.0344920000025013 + ], + "selfB": [ + 0.0454280000012659, + 0.03895899999770336, + 0.03473200000007637, + 0.03296000000045751, + 0.03528299999743467, + 0.03343999999924563, + 0.033501000001706416 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.3444701627735123, + "noiseRatio": 1.326360716338602, + "lowerRatio": 1.1064743393154834, + "upperRatio": 1.6336574237288684, + "candidate": [ + 0.06225300000369316, + 0.04447700000309851, + 0.04084100000181934, + 0.04450699999870267, + 0.0705870000019786, + 0.05752599999686936, + 0.03737600000022212 + ], + "baseline": [ + 0.04410599999391707, + 0.0357640000001993, + 0.037176000005274545, + 0.03373099999589613, + 0.03996099999494618, + 0.03532299999642419, + 0.034451999999873806 + ], + "selfA": [ + 0.03779699999722652, + 0.03780700000061188, + 0.06295500000123866, + 0.03412100000423379, + 0.03629499999806285, + 0.03231899999809684, + 0.03559300000051735 + ], + "selfB": [ + 0.03267000000050757, + 0.03413200000068173, + 0.04069100000197068, + 0.032167999997909646, + 0.032859999999345746, + 0.033250000000407454, + 0.032448999998450745 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.06413600000087172, + 0.035683999998582294, + 0.03358999999909429, + 0.040950999999040505, + 0.03813799999988987, + 0.03569399999832967, + 0.0344920000025013 + ], + "baselineSelfB": [ + 0.0454280000012659, + 0.03895899999770336, + 0.03473200000007637, + 0.03296000000045751, + 0.03528299999743467, + 0.03343999999924563, + 0.033501000001706416 + ] + }, + "historicalReference": { + "A": 9, + "U": 6, + "C": 31, + "plateSwitches": 8, + "commands": [ + [ + 2, + -3 + ], + [ + 5, + -6 + ], + [ + 3, + 4 + ], + [ + 2, + -6 + ], + [ + 4, + 2 + ], + [ + 1, + -2 + ], + [ + 5, + -3 + ], + [ + 3, + 3 + ], + [ + 0, + 2 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "silas-tavern-storeroom-door", + "title": "Tavern storeroom door", + "optimumA": 10, + "solvers": { + "originalBfs": { + "A": 10, + "U": 5, + "C": 31, + "plateSwitches": 9, + "commands": [ + [ + 3, + 2 + ], + [ + 5, + -3 + ], + [ + 1, + -3 + ], + [ + 5, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + 3 + ], + [ + 5, + -3 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 2, + -3 + ] + ], + "samplesMs": [ + 80.11304299999756, + 77.77798000000621, + 78.00689499999862, + 78.69170499999746, + 76.83120600000257, + 80.71428799999558, + 85.26358299999993 + ], + "timingMs": { + "min": 76.83120600000257, + "median": 78.69170499999746, + "p95": 85.26358299999993, + "max": 85.26358299999993 + } + }, + "upstream": { + "A": 10, + "U": 5, + "C": 31, + "plateSwitches": 9, + "commands": [ + [ + 3, + 2 + ], + [ + 5, + -3 + ], + [ + 1, + -3 + ], + [ + 5, + -3 + ], + [ + 2, + -3 + ], + [ + 5, + -3 + ], + [ + 4, + 3 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 2, + -3 + ] + ], + "samplesMs": [ + 23.187386999998125, + 23.839638999997987, + 23.705205999998725, + 22.879604000001564, + 23.425035000000207, + 27.695729000006395, + 23.709653000005346 + ], + "timingMs": { + "min": 22.879604000001564, + "median": 23.705205999998725, + "p95": 27.695729000006395, + "max": 27.695729000006395 + } + }, + "candidate": { + "A": 10, + "U": 5, + "C": 31, + "plateSwitches": 9, + "commands": [ + [ + 3, + 2 + ], + [ + 5, + -3 + ], + [ + 1, + -3 + ], + [ + 5, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + 3 + ], + [ + 5, + -3 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 2, + -3 + ] + ], + "samplesMs": [ + 0.16059099999984028, + 0.17933999999513617, + 0.15847800000483403, + 0.14888400000199908, + 0.1567060000015772, + 0.15615400000388036, + 0.15922999999747844 + ], + "timingMs": { + "min": 0.14888400000199908, + "median": 0.15847800000483403, + "p95": 0.17933999999513617, + "max": 0.17933999999513617 + } + }, + "baseline": { + "A": 10, + "U": 5, + "C": 31, + "plateSwitches": 9, + "commands": [ + [ + 3, + 2 + ], + [ + 5, + -3 + ], + [ + 1, + -3 + ], + [ + 5, + -3 + ], + [ + 2, + -3 + ], + [ + 4, + 3 + ], + [ + 5, + -3 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 2, + -3 + ] + ], + "samplesMs": [ + 0.14888300000166055, + 0.1705560000045807, + 0.1828449999957229, + 0.14683099999820115, + 0.15048699999897508, + 0.17150800000672461, + 0.1691149999969639 + ], + "timingMs": { + "min": 0.14683099999820115, + "median": 0.1691149999969639, + "p95": 0.1828449999957229, + "max": 0.1828449999957229 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.9834611811726517, + "noiseRatio": 1.1271243069466848, + "lowerRatio": 0.901740013471515, + "upperRatio": 1.0725884184178547 + }, + "timingAttempts": [ + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.9834611811726517, + "noiseRatio": 1.1271243069466848, + "lowerRatio": 0.901740013471515, + "upperRatio": 1.0725884184178547, + "candidate": [ + 0.16059099999984028, + 0.17933999999513617, + 0.15847800000483403, + 0.14888400000199908, + 0.1567060000015772, + 0.15615400000388036, + 0.15922999999747844 + ], + "baseline": [ + 0.14888300000166055, + 0.1705560000045807, + 0.1828449999957229, + 0.14683099999820115, + 0.15048699999897508, + 0.17150800000672461, + 0.1691149999969639 + ], + "selfA": [ + 0.0891540000011446, + 0.0846880000026431, + 0.08241399999678833, + 0.081372000000556, + 0.10190299999521812, + 0.08347499999945285, + 0.08200300000316929 + ], + "selfB": [ + 0.08291399999870919, + 0.08349599999928614, + 0.09510299999965355, + 0.08360600000014529, + 0.08433700000023236, + 0.0831560000005993, + 0.0832659999941825 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.0891540000011446, + 0.0846880000026431, + 0.08241399999678833, + 0.081372000000556, + 0.10190299999521812, + 0.08347499999945285, + 0.08200300000316929 + ], + "baselineSelfB": [ + 0.08291399999870919, + 0.08349599999928614, + 0.09510299999965355, + 0.08360600000014529, + 0.08433700000023236, + 0.0831560000005993, + 0.0832659999941825 + ] + }, + "historicalReference": { + "A": 10, + "U": 5, + "C": 31, + "plateSwitches": 9, + "commands": [ + [ + 3, + 2 + ], + [ + 5, + -3 + ], + [ + 1, + -3 + ], + [ + 5, + -3 + ], + [ + 2, + -3 + ], + [ + 5, + -3 + ], + [ + 4, + 3 + ], + [ + 1, + -4 + ], + [ + 5, + -4 + ], + [ + 2, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "jan-pond-house-chest", + "title": "Hut chest opposite Jan (New Camp)", + "optimumA": 8, + "solvers": { + "originalBfs": { + "A": 8, + "U": 6, + "C": 33, + "plateSwitches": 7, + "commands": [ + [ + 0, + -4 + ], + [ + 2, + -6 + ], + [ + 3, + 4 + ], + [ + 4, + 3 + ], + [ + 5, + -3 + ], + [ + 3, + 5 + ], + [ + 5, + -5 + ], + [ + 1, + 3 + ] + ], + "samplesMs": [ + 45.542411000002176, + 45.81365899999946, + 51.588528000000224, + 44.50916600000346, + 45.30073699999775, + 46.15811699999904, + 58.21535800000129 + ], + "timingMs": { + "min": 44.50916600000346, + "median": 45.81365899999946, + "p95": 58.21535800000129, + "max": 58.21535800000129 + } + }, + "upstream": { + "A": 8, + "U": 6, + "C": 33, + "plateSwitches": 7, + "commands": [ + [ + 3, + 4 + ], + [ + 5, + -3 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ], + [ + 3, + 5 + ], + [ + 5, + -5 + ], + [ + 0, + -4 + ], + [ + 1, + 3 + ] + ], + "samplesMs": [ + 51.14832399999432, + 52.83220799999981, + 51.68855800000165, + 51.66298900000402, + 56.487758999996004, + 51.530539999999746, + 51.48268800000369 + ], + "timingMs": { + "min": 51.14832399999432, + "median": 51.66298900000402, + "p95": 56.487758999996004, + "max": 56.487758999996004 + } + }, + "candidate": { + "A": 8, + "U": 6, + "C": 33, + "plateSwitches": 7, + "commands": [ + [ + 0, + -4 + ], + [ + 2, + -6 + ], + [ + 3, + 4 + ], + [ + 4, + 3 + ], + [ + 5, + -3 + ], + [ + 3, + 5 + ], + [ + 5, + -5 + ], + [ + 1, + 3 + ] + ], + "samplesMs": [ + 0.0833760000023176, + 0.08456700000533601, + 0.09170799999992596, + 0.08654999999998836, + 0.08488800000486663, + 0.08460699999704957, + 0.08499799999844981 + ], + "timingMs": { + "min": 0.0833760000023176, + "median": 0.08488800000486663, + "p95": 0.09170799999992596, + "max": 0.09170799999992596 + } + }, + "baseline": { + "A": 8, + "U": 6, + "C": 33, + "plateSwitches": 7, + "commands": [ + [ + 0, + -4 + ], + [ + 2, + -6 + ], + [ + 3, + 4 + ], + [ + 4, + 3 + ], + [ + 5, + -3 + ], + [ + 3, + 5 + ], + [ + 5, + -5 + ], + [ + 1, + 3 + ] + ], + "samplesMs": [ + 0.09716599999956088, + 0.0753130000011879, + 0.0742720000052941, + 0.07391099999949802, + 0.09722600000532111, + 0.07622499999706633, + 0.07314999999653082 + ], + "timingMs": { + "min": 0.07314999999653082, + "median": 0.0753130000011879, + "p95": 0.09722600000532111, + "max": 0.09722600000532111 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.0664434884312919, + "noiseRatio": 1.842180308877821, + "lowerRatio": 0.9143097756984165, + "upperRatio": 1.2438910140151889 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0664434884312919, + "noiseRatio": 1.842180308877821, + "lowerRatio": 0.9143097756984165, + "upperRatio": 1.2438910140151889, + "candidate": [ + 0.0833760000023176, + 0.08456700000533601, + 0.09170799999992596, + 0.08654999999998836, + 0.08488800000486663, + 0.08460699999704957, + 0.08499799999844981 + ], + "baseline": [ + 0.09716599999956088, + 0.0753130000011879, + 0.0742720000052941, + 0.07391099999949802, + 0.09722600000532111, + 0.07622499999706633, + 0.07314999999653082 + ], + "selfA": [ + 0.03774600000178907, + 0.0322189999933471, + 0.033620000001974404, + 0.03025599999818951, + 0.05900900000415277, + 0.04835300000559073, + 0.03581399999529822 + ], + "selfB": [ + 0.033230000000912696, + 0.032117999995534774, + 0.0320480000009411, + 0.03303999999479856, + 0.032859999999345746, + 0.15161799999623327, + 0.03429099999630125 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.1575456486374698, + "noiseRatio": 1.729128773037858, + "lowerRatio": 0.8125389147078709, + "upperRatio": 1.649043392784793, + "candidate": [ + 0.10885400000552181, + 0.04756100000668084, + 0.04406600000220351, + 0.03858799999579787, + 0.03550299999915296, + 0.03853800000069896, + 0.1106770000042161 + ], + "baseline": [ + 0.14290500000061002, + 0.04235299999709241, + 0.03642499999841675, + 0.03477300000668038, + 0.03739599999971688, + 0.03447199999936856, + 0.04844299999967916 + ], + "selfA": [ + 0.10960500000510365, + 0.03544399999373127, + 0.04343499999959022, + 0.03728599999885773, + 0.033740999999281485, + 0.03304999999818392, + 0.05491200000687968 + ], + "selfB": [ + 0.04831299999932526, + 0.03614399999787565, + 0.045968999998876825, + 0.034292000003915746, + 0.033320000002277084, + 0.05176799999753712, + 0.03471200000058161 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.03774600000178907, + 0.0322189999933471, + 0.033620000001974404, + 0.03025599999818951, + 0.05900900000415277, + 0.04835300000559073, + 0.03581399999529822 + ], + "baselineSelfB": [ + 0.033230000000912696, + 0.032117999995534774, + 0.0320480000009411, + 0.03303999999479856, + 0.032859999999345746, + 0.15161799999623327, + 0.03429099999630125 + ] + }, + "historicalReference": { + "A": 8, + "U": 6, + "C": 33, + "plateSwitches": 7, + "commands": [ + [ + 3, + 4 + ], + [ + 5, + -3 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ], + [ + 3, + 5 + ], + [ + 5, + -5 + ], + [ + 0, + -4 + ], + [ + 1, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "graham-chest", + "title": "Graham's chest", + "optimumA": 10, + "solvers": { + "originalBfs": { + "A": 10, + "U": 6, + "C": 40, + "plateSwitches": 9, + "commands": [ + [ + 1, + 5 + ], + [ + 4, + 6 + ], + [ + 0, + -5 + ], + [ + 1, + 6 + ], + [ + 4, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 2 + ], + [ + 3, + -3 + ], + [ + 4, + 2 + ], + [ + 5, + -1 + ] + ], + "samplesMs": [ + 97.51188099999854, + 100.20344099999784, + 97.98475300000428, + 97.99732100000256, + 97.42808399999922, + 99.88392000000022, + 111.1799899999969 + ], + "timingMs": { + "min": 97.42808399999922, + "median": 97.99732100000256, + "p95": 111.1799899999969, + "max": 111.1799899999969 + } + }, + "upstream": { + "A": 10, + "U": 6, + "C": 40, + "plateSwitches": 9, + "commands": [ + [ + 5, + -1 + ], + [ + 1, + 6 + ], + [ + 4, + 6 + ], + [ + 1, + 5 + ], + [ + 0, + -5 + ], + [ + 4, + 5 + ], + [ + 3, + -3 + ], + [ + 1, + 5 + ], + [ + 4, + 2 + ], + [ + 2, + 2 + ] + ], + "samplesMs": [ + 30.595291999998153, + 31.713304000004428, + 30.877466999998433, + 31.12507899999764, + 31.41895099999965, + 31.13688700000057, + 32.48808999999892 + ], + "timingMs": { + "min": 30.595291999998153, + "median": 31.13688700000057, + "p95": 32.48808999999892, + "max": 32.48808999999892 + } + }, + "candidate": { + "A": 10, + "U": 6, + "C": 40, + "plateSwitches": 9, + "commands": [ + [ + 1, + 5 + ], + [ + 4, + 6 + ], + [ + 0, + -5 + ], + [ + 1, + 6 + ], + [ + 4, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 2 + ], + [ + 3, + -3 + ], + [ + 4, + 2 + ], + [ + 5, + -1 + ] + ], + "samplesMs": [ + 0.09199899999657646, + 0.09075699999812059, + 0.09119700000155717, + 0.09116799999901559, + 0.09144799999921815, + 0.08807200000592275, + 0.09309999999823049 + ], + "timingMs": { + "min": 0.08807200000592275, + "median": 0.09119700000155717, + "p95": 0.09309999999823049, + "max": 0.09309999999823049 + } + }, + "baseline": { + "A": 10, + "U": 6, + "C": 40, + "plateSwitches": 9, + "commands": [ + [ + 1, + 5 + ], + [ + 4, + 6 + ], + [ + 0, + -5 + ], + [ + 1, + 6 + ], + [ + 4, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 2 + ], + [ + 3, + -3 + ], + [ + 4, + 2 + ], + [ + 5, + -1 + ] + ], + "samplesMs": [ + 0.08312499999738066, + 0.0822039999984554, + 0.08281500000157394, + 0.0991989999965881, + 0.08289399999921443, + 0.08142300000326941, + 0.08016000000498025 + ], + "timingMs": { + "min": 0.08016000000498025, + "median": 0.08281500000157394, + "p95": 0.0991989999965881, + "max": 0.0991989999965881 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.0800045710243187, + "noiseRatio": 1.049300965877078, + "lowerRatio": 0.9988650879646944, + "upperRatio": 1.1677351501093312 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0800045710243187, + "noiseRatio": 1.049300965877078, + "lowerRatio": 0.9988650879646944, + "upperRatio": 1.1677351501093312, + "candidate": [ + 0.09199899999657646, + 0.09075699999812059, + 0.09119700000155717, + 0.09116799999901559, + 0.09144799999921815, + 0.08807200000592275, + 0.09309999999823049 + ], + "baseline": [ + 0.08312499999738066, + 0.0822039999984554, + 0.08281500000157394, + 0.0991989999965881, + 0.08289399999921443, + 0.08142300000326941, + 0.08016000000498025 + ], + "selfA": [ + 0.044937999999092426, + 0.03505299999960698, + 0.035894000000553206, + 0.03513300000486197, + 0.03538300000218442, + 0.03479199999856064, + 0.035112000005028676 + ], + "selfB": [ + 0.044115999997302424, + 0.03825700000015786, + 0.034681999997701496, + 0.0341820000030566, + 0.035862999997334555, + 0.03529300000082003, + 0.03511300000536721 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.968048234600079, + "noiseRatio": 2.265419728250408, + "lowerRatio": 0.7026228026645848, + "upperRatio": 1.333741775755728, + "candidate": [ + 0.04287399999884656, + 0.05362099999911152, + 0.05537299999559764, + 0.04906399999890709, + 0.047962000004190486, + 0.048112999997101724, + 0.045178000000305474 + ], + "baseline": [ + 0.04477699999551987, + 0.03931900000316091, + 0.05411100000492297, + 0.08370599999761907, + 0.04502700000011828, + 0.0365349999992759, + 0.06230399999913061 + ], + "selfA": [ + 0.037486000001081266, + 0.0357640000001993, + 0.04923399999825051, + 0.05193800000415649, + 0.03757700000278419, + 0.03607400000328198, + 0.04441699999733828 + ], + "selfB": [ + 0.07458200000110082, + 0.03622500000346918, + 0.17764699999679578, + 0.04055200000584591, + 0.03741699999955017, + 0.03860800000256859, + 0.04309500000090338 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.044937999999092426, + 0.03505299999960698, + 0.035894000000553206, + 0.03513300000486197, + 0.03538300000218442, + 0.03479199999856064, + 0.035112000005028676 + ], + "baselineSelfB": [ + 0.044115999997302424, + 0.03825700000015786, + 0.034681999997701496, + 0.0341820000030566, + 0.035862999997334555, + 0.03529300000082003, + 0.03511300000536721 + ] + }, + "historicalReference": { + "A": 10, + "U": 6, + "C": 40, + "plateSwitches": 9, + "commands": [ + [ + 5, + -1 + ], + [ + 1, + 6 + ], + [ + 4, + 6 + ], + [ + 1, + 5 + ], + [ + 0, + -5 + ], + [ + 4, + 5 + ], + [ + 3, + -3 + ], + [ + 1, + 5 + ], + [ + 4, + 2 + ], + [ + 2, + 2 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "gravo-chest", + "title": "Gravo's chest", + "optimumA": 8, + "solvers": { + "originalBfs": { + "A": 8, + "U": 5, + "C": 27, + "plateSwitches": 7, + "commands": [ + [ + 0, + -5 + ], + [ + 4, + -1 + ], + [ + 1, + 3 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + -6 + ], + [ + 1, + 3 + ] + ], + "samplesMs": [ + 5.060563999999431, + 6.205956999998307, + 5.36050499999692, + 6.018434999998135, + 5.078320000000531, + 6.04823999999644, + 5.1621760000052745 + ], + "timingMs": { + "min": 5.060563999999431, + "median": 5.36050499999692, + "p95": 6.205956999998307, + "max": 6.205956999998307 + } + }, + "upstream": { + "A": 8, + "U": 5, + "C": 27, + "plateSwitches": 7, + "commands": [ + [ + 4, + -1 + ], + [ + 1, + 3 + ], + [ + 0, + -5 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 4, + -6 + ], + [ + 3, + 3 + ], + [ + 1, + 3 + ] + ], + "samplesMs": [ + 5.411072000002605, + 5.99046300000191, + 5.337070000001404, + 5.183919000002788, + 5.227435000000696, + 5.3317320000060135, + 5.57223299999896 + ], + "timingMs": { + "min": 5.183919000002788, + "median": 5.337070000001404, + "p95": 5.99046300000191, + "max": 5.99046300000191 + } + }, + "candidate": { + "A": 8, + "U": 5, + "C": 27, + "plateSwitches": 7, + "commands": [ + [ + 0, + -5 + ], + [ + 4, + -1 + ], + [ + 1, + 3 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + -6 + ], + [ + 1, + 3 + ] + ], + "samplesMs": [ + 0.05555399999866495, + 0.052237999996577855, + 0.06302500000310829, + 0.07672599999932572, + 0.0808919999981299, + 0.05266000000119675, + 0.06613899999501882 + ], + "timingMs": { + "min": 0.052237999996577855, + "median": 0.06302500000310829, + "p95": 0.0808919999981299, + "max": 0.0808919999981299 + } + }, + "baseline": { + "A": 8, + "U": 5, + "C": 27, + "plateSwitches": 7, + "commands": [ + [ + 0, + -5 + ], + [ + 4, + -1 + ], + [ + 1, + 3 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + -6 + ], + [ + 1, + 3 + ] + ], + "samplesMs": [ + 0.05351100000552833, + 0.050565999998070765, + 0.06857399999717018, + 0.0709869999991497, + 0.05471199999738019, + 0.05368000000453321, + 0.06633000000147149 + ], + "timingMs": { + "min": 0.050565999998070765, + "median": 0.05471199999738019, + "p95": 0.0709869999991497, + "max": 0.0709869999991497 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.063707752424634, + "noiseRatio": 2.743891970316831, + "lowerRatio": 0.9050448704312891, + "upperRatio": 1.2501857306025885 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.063707752424634, + "noiseRatio": 2.743891970316831, + "lowerRatio": 0.9050448704312891, + "upperRatio": 1.2501857306025885, + "candidate": [ + 0.05555399999866495, + 0.052237999996577855, + 0.06302500000310829, + 0.07672599999932572, + 0.0808919999981299, + 0.05266000000119675, + 0.06613899999501882 + ], + "baseline": [ + 0.05351100000552833, + 0.050565999998070765, + 0.06857399999717018, + 0.0709869999991497, + 0.05471199999738019, + 0.05368000000453321, + 0.06633000000147149 + ], + "selfA": [ + 0.02703099999780534, + 0.03684600000269711, + 0.03110700000252109, + 0.04055099999823142, + 0.03117700000439072, + 0.060490999996545725, + 0.03038599999854341 + ], + "selfB": [ + 0.03544300000066869, + 0.030075000002398156, + 0.027602000001934357, + 0.2703669999973499, + 0.030645999999251217, + 0.03443200000037905, + 0.027862000002642162 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0248126118746486, + "noiseRatio": 1.1235945098222313, + "lowerRatio": 0.805498042529257, + "upperRatio": 1.3038403993628487, + "candidate": [ + 0.03431099999579601, + 0.037606999998388346, + 0.03167799999937415, + 0.032669000000169035, + 0.034120999996957835, + 0.03345100000296952, + 0.029673999997612555 + ], + "baseline": [ + 0.05167800000344869, + 0.02889399999548914, + 0.03154700000595767, + 0.03694599999289494, + 0.028712999999697786, + 0.028151999998954125, + 0.02708100000018021 + ], + "selfA": [ + 0.03289000000222586, + 0.02740099999937229, + 0.03069600000162609, + 0.028783000001567416, + 0.02708100000018021, + 0.02731100000528386, + 0.02778099999704864 + ], + "selfB": [ + 0.026760999993712176, + 0.02837300000101095, + 0.031186000000161584, + 0.031217000003380235, + 0.026129000005312264, + 0.027701999999408145, + 0.026639999996405095 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.02703099999780534, + 0.03684600000269711, + 0.03110700000252109, + 0.04055099999823142, + 0.03117700000439072, + 0.060490999996545725, + 0.03038599999854341 + ], + "baselineSelfB": [ + 0.03544300000066869, + 0.030075000002398156, + 0.027602000001934357, + 0.2703669999973499, + 0.030645999999251217, + 0.03443200000037905, + 0.027862000002642162 + ] + }, + "historicalReference": { + "A": 8, + "U": 5, + "C": 27, + "plateSwitches": 7, + "commands": [ + [ + 4, + -1 + ], + [ + 1, + 3 + ], + [ + 0, + -5 + ], + [ + 2, + -3 + ], + [ + 1, + 3 + ], + [ + 4, + -6 + ], + [ + 3, + 3 + ], + [ + 1, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "diego-chest", + "title": "Diego's chest", + "optimumA": 11, + "solvers": { + "originalBfs": { + "A": 11, + "U": 6, + "C": 40, + "plateSwitches": 10, + "commands": [ + [ + 0, + 2 + ], + [ + 1, + 1 + ], + [ + 2, + -6 + ], + [ + 3, + -1 + ], + [ + 1, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + -6 + ], + [ + 5, + 3 + ], + [ + 1, + 6 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ] + ], + "samplesMs": [ + 78.02613399999973, + 81.06815999999526, + 84.98362199999974, + 79.10918500000116, + 81.41457500000251, + 83.85891399999673, + 84.8131499999945 + ], + "timingMs": { + "min": 78.02613399999973, + "median": 81.41457500000251, + "p95": 84.98362199999974, + "max": 84.98362199999974 + } + }, + "upstream": { + "A": 11, + "U": 6, + "C": 40, + "plateSwitches": 10, + "commands": [ + [ + 1, + 1 + ], + [ + 3, + -1 + ], + [ + 2, + -6 + ], + [ + 1, + 3 + ], + [ + 4, + 3 + ], + [ + 5, + 3 + ], + [ + 2, + -6 + ], + [ + 1, + 6 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ], + [ + 0, + 2 + ] + ], + "samplesMs": [ + 32.76997400000255, + 31.144458000002487, + 47.63711399999738, + 39.21390999999858, + 30.63828300000023, + 31.230564999998023, + 31.128260000004957 + ], + "timingMs": { + "min": 30.63828300000023, + "median": 31.230564999998023, + "p95": 47.63711399999738, + "max": 47.63711399999738 + } + }, + "candidate": { + "A": 11, + "U": 6, + "C": 40, + "plateSwitches": 10, + "commands": [ + [ + 0, + 2 + ], + [ + 1, + 1 + ], + [ + 2, + -6 + ], + [ + 3, + -1 + ], + [ + 1, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + -6 + ], + [ + 5, + 3 + ], + [ + 1, + 6 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ] + ], + "samplesMs": [ + 0.1474820000003092, + 0.1472009999997681, + 0.14838400000007823, + 0.14461799999844516, + 0.17976099999941653, + 0.14545899999939138, + 0.14338600000337465 + ], + "timingMs": { + "min": 0.14338600000337465, + "median": 0.1472009999997681, + "p95": 0.17976099999941653, + "max": 0.17976099999941653 + } + }, + "baseline": { + "A": 11, + "U": 6, + "C": 40, + "plateSwitches": 10, + "commands": [ + [ + 0, + 2 + ], + [ + 1, + 1 + ], + [ + 2, + -6 + ], + [ + 3, + -1 + ], + [ + 1, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + -6 + ], + [ + 5, + 3 + ], + [ + 1, + 6 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ] + ], + "samplesMs": [ + 0.17654599999514176, + 0.1535310000035679, + 0.13709600000584032, + 0.1633559999972931, + 0.13717600000381935, + 0.14910400000371737, + 0.13600399999995716 + ], + "timingMs": { + "min": 0.13600399999995716, + "median": 0.14910400000371737, + "p95": 0.17654599999514176, + "max": 0.17654599999514176 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 1.0048369183719719, + "noiseRatio": 1.218232763484805, + "lowerRatio": 0.8602364107765427, + "upperRatio": 1.1737438916492953 + }, + "timingAttempts": [ + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 1.0048369183719719, + "noiseRatio": 1.218232763484805, + "lowerRatio": 0.8602364107765427, + "upperRatio": 1.1737438916492953, + "candidate": [ + 0.1474820000003092, + 0.1472009999997681, + 0.14838400000007823, + 0.14461799999844516, + 0.17976099999941653, + 0.14545899999939138, + 0.14338600000337465 + ], + "baseline": [ + 0.17654599999514176, + 0.1535310000035679, + 0.13709600000584032, + 0.1633559999972931, + 0.13717600000381935, + 0.14910400000371737, + 0.13600399999995716 + ], + "selfA": [ + 0.08095199999661418, + 0.07380099999863887, + 0.07164799999736715, + 0.07334100000298349, + 0.07056599999486934, + 0.08489800000097603, + 0.09023599999636644 + ], + "selfB": [ + 0.07590399999753572, + 0.07343100000434788, + 0.08940499999880558, + 0.07465200000297045, + 0.0691339999975753, + 0.07150700000056531, + 0.11747599999944214 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.08095199999661418, + 0.07380099999863887, + 0.07164799999736715, + 0.07334100000298349, + 0.07056599999486934, + 0.08489800000097603, + 0.09023599999636644 + ], + "baselineSelfB": [ + 0.07590399999753572, + 0.07343100000434788, + 0.08940499999880558, + 0.07465200000297045, + 0.0691339999975753, + 0.07150700000056531, + 0.11747599999944214 + ] + }, + "historicalReference": { + "A": 11, + "U": 6, + "C": 40, + "plateSwitches": 10, + "commands": [ + [ + 1, + 1 + ], + [ + 3, + -1 + ], + [ + 2, + -6 + ], + [ + 1, + 3 + ], + [ + 4, + 3 + ], + [ + 5, + 3 + ], + [ + 2, + -6 + ], + [ + 1, + 6 + ], + [ + 2, + -6 + ], + [ + 4, + 3 + ], + [ + 0, + 2 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "grim-chest", + "title": "Grim's chest", + "optimumA": 8, + "solvers": { + "originalBfs": { + "A": 8, + "U": 6, + "C": 21, + "plateSwitches": 7, + "commands": [ + [ + 0, + -4 + ], + [ + 1, + 5 + ], + [ + 3, + 5 + ], + [ + 0, + -2 + ], + [ + 1, + 1 + ], + [ + 2, + -1 + ], + [ + 4, + -2 + ], + [ + 5, + -1 + ] + ], + "samplesMs": [ + 123.99482600000192, + 106.83830999999918, + 110.33234799999627, + 106.63469299999997, + 106.61467199999606, + 108.50407700000505, + 107.13202199999796 + ], + "timingMs": { + "min": 106.61467199999606, + "median": 107.13202199999796, + "p95": 123.99482600000192, + "max": 123.99482600000192 + } + }, + "upstream": { + "A": 8, + "U": 6, + "C": 21, + "plateSwitches": 7, + "commands": [ + [ + 1, + 4 + ], + [ + 0, + -3 + ], + [ + 3, + 5 + ], + [ + 5, + -1 + ], + [ + 4, + -2 + ], + [ + 2, + -1 + ], + [ + 1, + 2 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 22.178500999994867, + 22.13407400000142, + 22.90026599999692, + 22.155064999999013, + 23.2216499999995, + 22.792263000003004, + 23.271536000000197 + ], + "timingMs": { + "min": 22.13407400000142, + "median": 22.792263000003004, + "p95": 23.271536000000197, + "max": 23.271536000000197 + } + }, + "candidate": { + "A": 8, + "U": 6, + "C": 21, + "plateSwitches": 7, + "commands": [ + [ + 4, + -2 + ], + [ + 3, + 1 + ], + [ + 1, + 6 + ], + [ + 2, + -1 + ], + [ + 0, + -5 + ], + [ + 3, + 4 + ], + [ + 0, + -1 + ], + [ + 5, + -1 + ] + ], + "samplesMs": [ + 0.2403919999997015, + 0.22331600000325125, + 0.19663600000058068, + 0.2736520000034943, + 0.19669700000667945, + 0.22057199999835575, + 0.1844280000004801 + ], + "timingMs": { + "min": 0.1844280000004801, + "median": 0.22057199999835575, + "p95": 0.2736520000034943, + "max": 0.2736520000034943 + } + }, + "baseline": { + "A": 8, + "U": 6, + "C": 21, + "plateSwitches": 7, + "commands": [ + [ + 4, + -2 + ], + [ + 3, + 1 + ], + [ + 1, + 6 + ], + [ + 2, + -1 + ], + [ + 0, + -5 + ], + [ + 3, + 4 + ], + [ + 0, + -1 + ], + [ + 5, + -1 + ] + ], + "samplesMs": [ + 0.2888749999983702, + 0.18909500000154367, + 0.18198399999528192, + 0.20887399999628542, + 0.20872399999643676, + 0.18437700000504265, + 0.19606499999645166 + ], + "timingMs": { + "min": 0.18198399999528192, + "median": 0.19606499999645166, + "p95": 0.2888749999983702, + "max": 0.2888749999983702 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.0571268346755425, + "noiseRatio": 1.5388577923341638, + "lowerRatio": 0.8915404357448891, + "upperRatio": 1.2534677057664099 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0571268346755425, + "noiseRatio": 1.5388577923341638, + "lowerRatio": 0.8915404357448891, + "upperRatio": 1.2534677057664099, + "candidate": [ + 0.2403919999997015, + 0.22331600000325125, + 0.19663600000058068, + 0.2736520000034943, + 0.19669700000667945, + 0.22057199999835575, + 0.1844280000004801 + ], + "baseline": [ + 0.2888749999983702, + 0.18909500000154367, + 0.18198399999528192, + 0.20887399999628542, + 0.20872399999643676, + 0.18437700000504265, + 0.19606499999645166 + ], + "selfA": [ + 0.1466710000022431, + 0.17328100000304403, + 0.1293149999983143, + 0.1080530000035651, + 0.10702100000344217, + 0.20902399999613408, + 0.18371599999954924 + ], + "selfB": [ + 0.13409200000023702, + 0.11385100000188686, + 0.1254289999997127, + 0.11034600000130013, + 0.10189399999944726, + 0.11756600000080653, + 0.14673100000072736 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.2244016265779087, + "noiseRatio": 1.5237638252159087, + "lowerRatio": 0.9993279779577071, + "upperRatio": 1.5001674887862242, + "candidate": [ + 0.22357699999702163, + 0.19327999999950407, + 0.14245500000106404, + 0.12996600000042235, + 0.1362550000048941, + 0.2415739999996731, + 0.1392889999988256 + ], + "baseline": [ + 0.17612499999813735, + 0.13331999999354593, + 0.12535899999784306, + 0.12398699999903329, + 0.13057699999626493, + 0.14042099999642232, + 0.1328700000012759 + ], + "selfA": [ + 0.13919999999779975, + 0.3052499999976135, + 0.14546899999550078, + 0.11716600000363542, + 0.12280499999906169, + 0.12243400000443216, + 0.14620999999897322 + ], + "selfB": [ + 0.15113800000108313, + 0.14486800000304356, + 0.13150799999857554, + 0.11893899999995483, + 0.1171360000007553, + 0.14773199999763165, + 0.12983499999972992 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.1466710000022431, + 0.17328100000304403, + 0.1293149999983143, + 0.1080530000035651, + 0.10702100000344217, + 0.20902399999613408, + 0.18371599999954924 + ], + "baselineSelfB": [ + 0.13409200000023702, + 0.11385100000188686, + 0.1254289999997127, + 0.11034600000130013, + 0.10189399999944726, + 0.11756600000080653, + 0.14673100000072736 + ] + }, + "historicalReference": { + "A": 8, + "U": 6, + "C": 21, + "plateSwitches": 7, + "commands": [ + [ + 1, + 4 + ], + [ + 0, + -3 + ], + [ + 3, + 5 + ], + [ + 5, + -1 + ], + [ + 4, + -2 + ], + [ + 2, + -1 + ], + [ + 1, + 2 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "mercenary-near-grim-chest", + "title": "Mercenary's chest (by Grim)", + "optimumA": 8, + "solvers": { + "originalBfs": { + "A": 8, + "U": 6, + "C": 25, + "plateSwitches": 7, + "commands": [ + [ + 2, + 3 + ], + [ + 1, + 3 + ], + [ + 3, + -6 + ], + [ + 5, + 6 + ], + [ + 0, + -2 + ], + [ + 3, + -2 + ], + [ + 4, + 2 + ], + [ + 5, + 1 + ] + ], + "samplesMs": [ + 103.6997210000045, + 115.46138799999608, + 107.06817600000068, + 102.32011000000057, + 100.22756999999547, + 100.69973200000095, + 111.03184100000362 + ], + "timingMs": { + "min": 100.22756999999547, + "median": 103.6997210000045, + "p95": 115.46138799999608, + "max": 115.46138799999608 + } + }, + "upstream": { + "A": 8, + "U": 6, + "C": 25, + "plateSwitches": 7, + "commands": [ + [ + 3, + -6 + ], + [ + 5, + 6 + ], + [ + 4, + 2 + ], + [ + 3, + -2 + ], + [ + 5, + 1 + ], + [ + 2, + 3 + ], + [ + 1, + 3 + ], + [ + 0, + -2 + ] + ], + "samplesMs": [ + 30.50765699999465, + 29.63531299999886, + 34.25368400000298, + 31.108320999999705, + 32.98959199999808, + 35.38451599999826, + 35.12606199999573 + ], + "timingMs": { + "min": 29.63531299999886, + "median": 32.98959199999808, + "p95": 35.38451599999826, + "max": 35.38451599999826 + } + }, + "candidate": { + "A": 8, + "U": 6, + "C": 25, + "plateSwitches": 7, + "commands": [ + [ + 2, + 3 + ], + [ + 1, + 3 + ], + [ + 3, + -6 + ], + [ + 5, + 6 + ], + [ + 0, + -2 + ], + [ + 3, + -2 + ], + [ + 4, + 2 + ], + [ + 5, + 1 + ] + ], + "samplesMs": [ + 0.09011499999905936, + 0.10735199999908218, + 0.09140800000022864, + 0.12449699999706354, + 0.08769199999369448, + 0.12648099999933038, + 0.0907459999943967 + ], + "timingMs": { + "min": 0.08769199999369448, + "median": 0.09140800000022864, + "p95": 0.12648099999933038, + "max": 0.12648099999933038 + } + }, + "baseline": { + "A": 8, + "U": 6, + "C": 25, + "plateSwitches": 7, + "commands": [ + [ + 2, + 3 + ], + [ + 1, + 3 + ], + [ + 3, + -6 + ], + [ + 5, + 6 + ], + [ + 0, + -2 + ], + [ + 3, + -2 + ], + [ + 4, + 2 + ], + [ + 5, + 1 + ] + ], + "samplesMs": [ + 0.09585399999923538, + 0.07507299999997485, + 0.07295999999769265, + 0.09478300000046147, + 0.0757240000020829, + 0.07189800000196556, + 0.070596999998088 + ], + "timingMs": { + "min": 0.070596999998088, + "median": 0.07507299999997485, + "p95": 0.09585399999923538, + "max": 0.09585399999923538 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.2852516481076364, + "noiseRatio": 1.4294240145276385, + "lowerRatio": 1.0514720451417177, + "upperRatio": 1.5710087648985054 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.2852516481076364, + "noiseRatio": 1.4294240145276385, + "lowerRatio": 1.0514720451417177, + "upperRatio": 1.5710087648985054, + "candidate": [ + 0.09011499999905936, + 0.10735199999908218, + 0.09140800000022864, + 0.12449699999706354, + 0.08769199999369448, + 0.12648099999933038, + 0.0907459999943967 + ], + "baseline": [ + 0.09585399999923538, + 0.07507299999997485, + 0.07295999999769265, + 0.09478300000046147, + 0.0757240000020829, + 0.07189800000196556, + 0.070596999998088 + ], + "selfA": [ + 0.059769999999844, + 0.08783199999743374, + 0.04704100000526523, + 0.03536399999575224, + 0.035734000004595146, + 0.03413100000034319, + 0.031778000004123896 + ], + "selfB": [ + 0.043356000001949724, + 0.05503300000418676, + 0.03664500000013504, + 0.03427100000408245, + 0.03306000000156928, + 0.034831999997550156, + 0.03167799999937415 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.1095127548243418, + "noiseRatio": 1.3047741061979186, + "lowerRatio": 0.8619348051404087, + "upperRatio": 1.4282037873123912, + "candidate": [ + 0.07118700000137324, + 0.07404200000019046, + 0.07116800000221701, + 0.06659100000251783, + 0.07129799999529496, + 0.07397199999832083, + 0.06968400000187103 + ], + "baseline": [ + 0.06824200000119163, + 0.053640999998606276, + 0.06182299999636598, + 0.09897899999486981, + 0.06235400000150548, + 0.05517300000065006, + 0.05755700000008801 + ], + "selfA": [ + 0.06560899999749381, + 0.05266999999730615, + 0.0841870000003837, + 0.06650000000081491, + 0.06203300000197487, + 0.05512299999827519, + 0.058307999999669846 + ], + "selfB": [ + 0.05631500000163214, + 0.08580000000074506, + 0.0657089999949676, + 0.05546400000457652, + 0.057826999996905215, + 0.061923000001115724, + 0.06225300000369316 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.059769999999844, + 0.08783199999743374, + 0.04704100000526523, + 0.03536399999575224, + 0.035734000004595146, + 0.03413100000034319, + 0.031778000004123896 + ], + "baselineSelfB": [ + 0.043356000001949724, + 0.05503300000418676, + 0.03664500000013504, + 0.03427100000408245, + 0.03306000000156928, + 0.034831999997550156, + 0.03167799999937415 + ] + }, + "historicalReference": { + "A": 8, + "U": 6, + "C": 25, + "plateSwitches": 7, + "commands": [ + [ + 3, + -6 + ], + [ + 5, + 6 + ], + [ + 4, + 2 + ], + [ + 3, + -2 + ], + [ + 5, + 1 + ], + [ + 2, + 3 + ], + [ + 1, + 3 + ], + [ + 0, + -2 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "popular-lock", + "title": "Popular lock", + "optimumA": 13, + "solvers": { + "originalBfs": { + "A": 13, + "U": 5, + "C": 46, + "plateSwitches": 12, + "commands": [ + [ + 2, + -1 + ], + [ + 3, + -1 + ], + [ + 1, + -2 + ], + [ + 4, + -6 + ], + [ + 0, + 4 + ], + [ + 4, + -4 + ], + [ + 3, + -4 + ], + [ + 0, + 2 + ], + [ + 1, + -4 + ], + [ + 4, + -6 + ], + [ + 0, + 6 + ], + [ + 2, + -3 + ], + [ + 4, + -3 + ] + ], + "samplesMs": [ + 12.193998000002466, + 10.646194000000833, + 10.419943000000785, + 10.032790000004752, + 10.938302999995358, + 10.174182999995537, + 10.439902999998594 + ], + "timingMs": { + "min": 10.032790000004752, + "median": 10.439902999998594, + "p95": 12.193998000002466, + "max": 12.193998000002466 + } + }, + "upstream": { + "A": 13, + "U": 5, + "C": 46, + "plateSwitches": 12, + "commands": [ + [ + 4, + -4 + ], + [ + 3, + -2 + ], + [ + 2, + -1 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + 6 + ], + [ + 4, + -6 + ], + [ + 3, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + 3 + ], + [ + 4, + -6 + ], + [ + 2, + -3 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 6.413843000002089, + 5.8275809999977355, + 6.043526000001293, + 5.798356999999669, + 7.191613999995752, + 6.0573460000014165, + 6.129375999997137 + ], + "timingMs": { + "min": 5.798356999999669, + "median": 6.0573460000014165, + "p95": 7.191613999995752, + "max": 7.191613999995752 + } + }, + "candidate": { + "A": 13, + "U": 5, + "C": 46, + "plateSwitches": 12, + "commands": [ + [ + 4, + -4 + ], + [ + 2, + -1 + ], + [ + 3, + -2 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + 6 + ], + [ + 2, + -3 + ], + [ + 4, + -6 + ], + [ + 1, + -3 + ], + [ + 0, + 3 + ], + [ + 3, + -3 + ], + [ + 4, + -6 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 0.5802240000048187, + 0.5621759999994538, + 0.560563999999431, + 0.6886860000013257, + 0.7211650000026566, + 0.5734129999982542, + 0.5543850000030943 + ], + "timingMs": { + "min": 0.5543850000030943, + "median": 0.5734129999982542, + "p95": 0.7211650000026566, + "max": 0.7211650000026566 + } + }, + "baseline": { + "A": 13, + "U": 5, + "C": 46, + "plateSwitches": 12, + "commands": [ + [ + 4, + -4 + ], + [ + 2, + -1 + ], + [ + 3, + -2 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + 6 + ], + [ + 2, + -3 + ], + [ + 4, + -6 + ], + [ + 1, + -3 + ], + [ + 0, + 3 + ], + [ + 3, + -3 + ], + [ + 4, + -6 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 0.5761469999997644, + 0.5817449999958626, + 0.6215450000017881, + 0.5673040000037872, + 0.6296880000008969, + 0.6063819999981206, + 0.5995719999991707 + ], + "timingMs": { + "min": 0.5673040000037872, + "median": 0.5995719999991707, + "p95": 0.6296880000008969, + "max": 0.6296880000008969 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 1.0093062195973268, + "noiseRatio": 1.2108212795985236, + "lowerRatio": 0.896512263533293, + "upperRatio": 1.136291254849094 + }, + "timingAttempts": [ + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 1.0093062195973268, + "noiseRatio": 1.2108212795985236, + "lowerRatio": 0.896512263533293, + "upperRatio": 1.136291254849094, + "candidate": [ + 0.5802240000048187, + 0.5621759999994538, + 0.560563999999431, + 0.6886860000013257, + 0.7211650000026566, + 0.5734129999982542, + 0.5543850000030943 + ], + "baseline": [ + 0.5761469999997644, + 0.5817449999958626, + 0.6215450000017881, + 0.5673040000037872, + 0.6296880000008969, + 0.6063819999981206, + 0.5995719999991707 + ], + "selfA": [ + 0.49143000000185566, + 0.4836280000017723, + 0.4858010000025388, + 0.5723110000035376, + 0.7714409999971394, + 0.6432290000011562, + 0.5160260000047856 + ], + "selfB": [ + 0.5159870000061346, + 0.5590310000043246, + 0.4518499999976484, + 0.47976200000266545, + 0.8175200000041514, + 0.5127710000015213, + 0.45999299999675713 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.49143000000185566, + 0.4836280000017723, + 0.4858010000025388, + 0.5723110000035376, + 0.7714409999971394, + 0.6432290000011562, + 0.5160260000047856 + ], + "baselineSelfB": [ + 0.5159870000061346, + 0.5590310000043246, + 0.4518499999976484, + 0.47976200000266545, + 0.8175200000041514, + 0.5127710000015213, + 0.45999299999675713 + ] + }, + "historicalReference": { + "A": 13, + "U": 5, + "C": 46, + "plateSwitches": 12, + "commands": [ + [ + 4, + -4 + ], + [ + 3, + -2 + ], + [ + 2, + -1 + ], + [ + 1, + -3 + ], + [ + 4, + -3 + ], + [ + 0, + 6 + ], + [ + 4, + -6 + ], + [ + 3, + -3 + ], + [ + 1, + -3 + ], + [ + 0, + 3 + ], + [ + 4, + -6 + ], + [ + 2, + -3 + ], + [ + 0, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "alberto-mine-hut-chest", + "title": "Chest in Alberto's mine hut", + "optimumA": 9, + "solvers": { + "originalBfs": { + "A": 9, + "U": 5, + "C": 36, + "plateSwitches": 8, + "commands": [ + [ + 3, + -4 + ], + [ + 1, + -2 + ], + [ + 4, + 6 + ], + [ + 0, + -3 + ], + [ + 3, + -6 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + 3 + ] + ], + "samplesMs": [ + 15.54966300000524, + 9.159835999998904, + 9.576733000001695, + 9.728412000004028, + 9.481871000003594, + 9.502863000001526, + 9.873650000001362 + ], + "timingMs": { + "min": 9.159835999998904, + "median": 9.576733000001695, + "p95": 15.54966300000524, + "max": 15.54966300000524 + } + }, + "upstream": { + "A": 9, + "U": 5, + "C": 36, + "plateSwitches": 8, + "commands": [ + [ + 3, + -4 + ], + [ + 4, + 3 + ], + [ + 2, + -3 + ], + [ + 1, + -5 + ], + [ + 3, + -6 + ], + [ + 4, + 6 + ], + [ + 1, + -3 + ], + [ + 3, + -3 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 5.980021000003035, + 5.648862000001827, + 5.611265999999887, + 5.470332999997481, + 5.791577000003599, + 5.7830529999991995, + 5.695751999999629 + ], + "timingMs": { + "min": 5.470332999997481, + "median": 5.695751999999629, + "p95": 5.980021000003035, + "max": 5.980021000003035 + } + }, + "candidate": { + "A": 9, + "U": 5, + "C": 36, + "plateSwitches": 8, + "commands": [ + [ + 3, + -4 + ], + [ + 1, + -2 + ], + [ + 4, + 6 + ], + [ + 0, + -3 + ], + [ + 3, + -6 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + 3 + ] + ], + "samplesMs": [ + 0.05858799999987241, + 0.07028600000194274, + 0.06553799999528565, + 0.05515299999387935, + 0.05858799999987241, + 0.07267899999715155, + 0.06283499999699416 + ], + "timingMs": { + "min": 0.05515299999387935, + "median": 0.06283499999699416, + "p95": 0.07267899999715155, + "max": 0.07267899999715155 + } + }, + "baseline": { + "A": 9, + "U": 5, + "C": 36, + "plateSwitches": 8, + "commands": [ + [ + 3, + -4 + ], + [ + 1, + -2 + ], + [ + 4, + 6 + ], + [ + 0, + -3 + ], + [ + 3, + -6 + ], + [ + 1, + -6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ], + [ + 4, + 3 + ] + ], + "samplesMs": [ + 0.04632999999739695, + 0.10008099999686237, + 0.05662499999743886, + 0.045968999998876825, + 0.049414000000979286, + 0.1601809999992838, + 0.08660999999847263 + ], + "timingMs": { + "min": 0.045968999998876825, + "median": 0.05662499999743886, + "p95": 0.1601809999992838, + "max": 0.1601809999992838 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: regression. Conflicting verdicts remain inconclusive.", + "ratio": 0.9008130853445011, + "noiseRatio": 1.7548282742932664, + "lowerRatio": 0.598029839822097, + "upperRatio": 1.3568958615330553 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9008130853445011, + "noiseRatio": 1.7548282742932664, + "lowerRatio": 0.598029839822097, + "upperRatio": 1.3568958615330553, + "candidate": [ + 0.05858799999987241, + 0.07028600000194274, + 0.06553799999528565, + 0.05515299999387935, + 0.05858799999987241, + 0.07267899999715155, + 0.06283499999699416 + ], + "baseline": [ + 0.04632999999739695, + 0.10008099999686237, + 0.05662499999743886, + 0.045968999998876825, + 0.049414000000979286, + 0.1601809999992838, + 0.08660999999847263 + ], + "selfA": [ + 0.02981500000169035, + 0.02666000000317581, + 0.08006100000056904, + 0.022392999999283347, + 0.022013000001606997, + 0.028281999999308027, + 0.02891299999464536 + ], + "selfB": [ + 0.02895400000124937, + 0.028001999999105465, + 0.03077599999960512, + 0.0257290000008652, + 0.02392600000166567, + 0.02279399999679299, + 0.02276500000152737 + ] + }, + { + "verdict": "regression", + "reason": "Matched slowdown exceeds the unchanged-control uncertainty envelope.", + "ratio": 1.2917820899185781, + "noiseRatio": 1.1457017671607903, + "lowerRatio": 1.206108926568913, + "upperRatio": 1.383540848654075, + "candidate": [ + 0.03038599999854341, + 0.02932400000281632, + 0.03126699999847915, + 0.030795999999099877, + 0.028173000006063376, + 0.03356099999655271, + 0.03794700000435114 + ], + "baseline": [ + 0.02370599999994738, + 0.023716000003332738, + 0.02407600000151433, + 0.023725999999442138, + 0.022693999999319203, + 0.02264399999694433, + 0.03107599999930244 + ], + "selfA": [ + 0.022053000000596512, + 0.02208299999620067, + 0.02278400000068359, + 0.022224000000278465, + 0.027581999995163642, + 0.02278400000068359, + 0.031126000001677312 + ], + "selfB": [ + 0.022032999993825797, + 0.021942999999737367, + 0.02170299999852432, + 0.02270400000270456, + 0.0240159999957541, + 0.022022999997716397, + 0.040340999999898486 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.02981500000169035, + 0.02666000000317581, + 0.08006100000056904, + 0.022392999999283347, + 0.022013000001606997, + 0.028281999999308027, + 0.02891299999464536 + ], + "baselineSelfB": [ + 0.02895400000124937, + 0.028001999999105465, + 0.03077599999960512, + 0.0257290000008652, + 0.02392600000166567, + 0.02279399999679299, + 0.02276500000152737 + ] + }, + "historicalReference": { + "A": 9, + "U": 5, + "C": 36, + "plateSwitches": 8, + "commands": [ + [ + 3, + -4 + ], + [ + 4, + 3 + ], + [ + 2, + -3 + ], + [ + 1, + -5 + ], + [ + 3, + -6 + ], + [ + 4, + 6 + ], + [ + 1, + -3 + ], + [ + 3, + -3 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "alberto-mine-upstairs-left-chest", + "title": "Left chest upstairs in Alberto's mine", + "optimumA": 4, + "solvers": { + "originalBfs": { + "A": 4, + "U": 3, + "C": 20, + "plateSwitches": 3, + "commands": [ + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 1, + 6 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.039550000001327135, + 0.03953000000183238, + 0.037837000003491994, + 0.03751599999668542, + 0.03972000000067055, + 0.03649500000028638, + 0.04142200000205776 + ], + "timingMs": { + "min": 0.03649500000028638, + "median": 0.03953000000183238, + "p95": 0.04142200000205776, + "max": 0.04142200000205776 + } + }, + "upstream": { + "A": 4, + "U": 3, + "C": 20, + "plateSwitches": 3, + "commands": [ + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 1, + 6 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.21720700000150828, + 0.4312690000006114, + 0.21534400000382448, + 0.21983100000215927, + 0.20572000000538537, + 0.2105370000062976, + 0.2195099999953527 + ], + "timingMs": { + "min": 0.20572000000538537, + "median": 0.21720700000150828, + "p95": 0.4312690000006114, + "max": 0.4312690000006114 + } + }, + "candidate": { + "A": 4, + "U": 3, + "C": 20, + "plateSwitches": 3, + "commands": [ + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 1, + 6 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.0247079999971902, + 0.026470000004337635, + 0.02085100000113016, + 0.016284000004816335, + 0.021302000001014676, + 0.10232399999949848, + 0.018338000001676846 + ], + "timingMs": { + "min": 0.016284000004816335, + "median": 0.021302000001014676, + "p95": 0.10232399999949848, + "max": 0.10232399999949848 + } + }, + "baseline": { + "A": 4, + "U": 3, + "C": 20, + "plateSwitches": 3, + "commands": [ + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 1, + 6 + ], + [ + 3, + 3 + ] + ], + "samplesMs": [ + 0.020020999996631872, + 0.022412999998778105, + 0.016223999999056105, + 0.013990999999805354, + 0.018427000002702698, + 0.019458999995549675, + 0.015052000002469867 + ], + "timingMs": { + "min": 0.013990999999805354, + "median": 0.018427000002702698, + "p95": 0.022412999998778105, + "max": 0.022412999998778105 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.487925133999896, + "noiseRatio": 1.3979035144116803, + "lowerRatio": 0.8286198448781842, + "upperRatio": 2.6718177437737785 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.487925133999896, + "noiseRatio": 1.3979035144116803, + "lowerRatio": 0.8286198448781842, + "upperRatio": 2.6718177437737785, + "candidate": [ + 0.0247079999971902, + 0.026470000004337635, + 0.02085100000113016, + 0.016284000004816335, + 0.021302000001014676, + 0.10232399999949848, + 0.018338000001676846 + ], + "baseline": [ + 0.020020999996631872, + 0.022412999998778105, + 0.016223999999056105, + 0.013990999999805354, + 0.018427000002702698, + 0.019458999995549675, + 0.015052000002469867 + ], + "selfA": [ + 0.012769999993906822, + 0.011608000000705943, + 0.011457000000518747, + 0.02313500000309432, + 0.013951000000815839, + 0.01151699999900302, + 0.012879000001703389 + ], + "selfB": [ + 0.011626999999862164, + 0.011717000001226552, + 0.011488000003737397, + 0.013580999999248888, + 0.011837999998533633, + 0.011187000003701542, + 0.011016999997082166 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9358178677041551, + "noiseRatio": 1.2650722642512822, + "lowerRatio": 0.48306613142654853, + "upperRatio": 1.8129092986257356, + "candidate": [ + 0.014691999997012317, + 0.020320000003266614, + 0.01504199999908451, + 0.013961000004201196, + 0.013971000000310596, + 0.013741000002482906, + 0.01717599999392405 + ], + "baseline": [ + 0.012067999996361323, + 0.0885340000022552, + 0.012088000003132038, + 0.013689999999769498, + 0.012018000001262408, + 0.013019999998505227, + 0.011888000000908505 + ], + "selfA": [ + 0.011346999999659602, + 0.01651500000298256, + 0.012629000004380941, + 0.011418000001867767, + 0.011446999997133389, + 0.011446999997133389, + 0.016194000003451947 + ], + "selfB": [ + 0.011647999999695458, + 0.012398999999277294, + 0.011316999996779487, + 0.01151699999900302, + 0.011336999996274244, + 0.011758000000554603, + 0.012287999998079613 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.012769999993906822, + 0.011608000000705943, + 0.011457000000518747, + 0.02313500000309432, + 0.013951000000815839, + 0.01151699999900302, + 0.012879000001703389 + ], + "baselineSelfB": [ + 0.011626999999862164, + 0.011717000001226552, + 0.011488000003737397, + 0.013580999999248888, + 0.011837999998533633, + 0.011187000003701542, + 0.011016999997082166 + ] + }, + "historicalReference": { + "A": 4, + "U": 3, + "C": 20, + "plateSwitches": 3, + "commands": [ + [ + 1, + 5 + ], + [ + 2, + 6 + ], + [ + 1, + 6 + ], + [ + 3, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "alberto-mine-upstairs-right-chest", + "title": "Right chest upstairs in Alberto's mine", + "optimumA": 5, + "solvers": { + "originalBfs": { + "A": 5, + "U": 5, + "C": 13, + "plateSwitches": 4, + "commands": [ + [ + 0, + 3 + ], + [ + 1, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 4 + ], + [ + 3, + 2 + ] + ], + "samplesMs": [ + 3.2137870000005933, + 2.1705670000010286, + 2.239350000003469, + 2.7121420000039507, + 2.7879759999996168, + 3.270541999998386, + 2.3086849999963306 + ], + "timingMs": { + "min": 2.1705670000010286, + "median": 2.7121420000039507, + "p95": 3.270541999998386, + "max": 3.270541999998386 + } + }, + "upstream": { + "A": 5, + "U": 5, + "C": 13, + "plateSwitches": 4, + "commands": [ + [ + 0, + 3 + ], + [ + 2, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 4 + ], + [ + 3, + 2 + ] + ], + "samplesMs": [ + 1.6186859999943408, + 1.7235040000014124, + 1.7385770000037155, + 1.7366640000036568, + 1.9132389999940642, + 1.6731179999987944, + 1.650394000003871 + ], + "timingMs": { + "min": 1.6186859999943408, + "median": 1.7235040000014124, + "p95": 1.9132389999940642, + "max": 1.9132389999940642 + } + }, + "candidate": { + "A": 5, + "U": 5, + "C": 13, + "plateSwitches": 4, + "commands": [ + [ + 0, + 3 + ], + [ + 1, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 4 + ], + [ + 3, + 2 + ] + ], + "samplesMs": [ + 0.03578399999969406, + 0.029415000004519243, + 0.02674999999726424, + 0.03757700000278419, + 0.03501300000061747, + 0.2792909999989206, + 0.0316469999961555 + ], + "timingMs": { + "min": 0.02674999999726424, + "median": 0.03501300000061747, + "p95": 0.2792909999989206, + "max": 0.2792909999989206 + } + }, + "baseline": { + "A": 5, + "U": 5, + "C": 13, + "plateSwitches": 4, + "commands": [ + [ + 0, + 3 + ], + [ + 1, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 4 + ], + [ + 3, + 2 + ] + ], + "samplesMs": [ + 0.02636000000347849, + 0.023385000000416767, + 0.023885999995400198, + 0.045328000000154134, + 0.03139799999917159, + 0.027211000000534113, + 0.025418000004719943 + ], + "timingMs": { + "min": 0.023385000000416767, + "median": 0.02636000000347849, + "p95": 0.045328000000154134, + "max": 0.045328000000154134 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.5610623841521194, + "noiseRatio": 1.1108149875916935, + "lowerRatio": 0.6429848726381551, + "upperRatio": 3.790004821133784 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.5610623841521194, + "noiseRatio": 1.1108149875916935, + "lowerRatio": 0.6429848726381551, + "upperRatio": 3.790004821133784, + "candidate": [ + 0.03578399999969406, + 0.029415000004519243, + 0.02674999999726424, + 0.03757700000278419, + 0.03501300000061747, + 0.2792909999989206, + 0.0316469999961555 + ], + "baseline": [ + 0.02636000000347849, + 0.023385000000416767, + 0.023885999995400198, + 0.045328000000154134, + 0.03139799999917159, + 0.027211000000534113, + 0.025418000004719943 + ], + "selfA": [ + 0.017515999999886844, + 0.013208999997004867, + 0.012889999998151325, + 0.013480000001436565, + 0.012798999996448401, + 0.013169999998353887, + 0.012979999999515712 + ], + "selfB": [ + 0.014332000006106682, + 0.01344000000244705, + 0.01300999999511987, + 0.013399999996181577, + 0.013559999999415595, + 0.014682000000902917, + 0.012808999999833759 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.276538956617125, + "noiseRatio": 1.1962433562633301, + "lowerRatio": 1.02174388626455, + "upperRatio": 1.5948729712674932, + "candidate": [ + 0.020389999997860286, + 0.02134200000000419, + 0.01748599999700673, + 0.016804999999294523, + 0.01840799999627052, + 0.017376000003423542, + 0.01685600000200793 + ], + "baseline": [ + 0.013691000000108033, + 0.013359999997192062, + 0.013740999995206948, + 0.012949999996635597, + 0.014220999997633044, + 0.013139999995473772, + 0.020521000005828682 + ], + "selfA": [ + 0.013801000000967178, + 0.019308999995701015, + 0.013579999998910353, + 0.012778999996953644, + 0.012710000002698507, + 0.01292000000103144, + 0.014241999997466337 + ], + "selfB": [ + 0.013429999999061693, + 0.014272000000346452, + 0.012859000002208631, + 0.012949000003573019, + 0.012960000000020955, + 0.013980999996419996, + 0.01314999999885913 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.017515999999886844, + 0.013208999997004867, + 0.012889999998151325, + 0.013480000001436565, + 0.012798999996448401, + 0.013169999998353887, + 0.012979999999515712 + ], + "baselineSelfB": [ + 0.014332000006106682, + 0.01344000000244705, + 0.01300999999511987, + 0.013399999996181577, + 0.013559999999415595, + 0.014682000000902917, + 0.012808999999833759 + ] + }, + "historicalReference": { + "A": 5, + "U": 5, + "C": 13, + "plateSwitches": 4, + "commands": [ + [ + 0, + 3 + ], + [ + 2, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 4 + ], + [ + 3, + 2 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "cavalorn-storeroom-door", + "title": "Hunter Cavalorn's storeroom door", + "optimumA": 11, + "solvers": { + "originalBfs": { + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 0, + -1 + ], + [ + 3, + -2 + ], + [ + 1, + -6 + ], + [ + 5, + 4 + ], + [ + 0, + -4 + ], + [ + 3, + -2 + ], + [ + 1, + -6 + ], + [ + 4, + -4 + ], + [ + 5, + 6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ] + ], + "samplesMs": [ + 89.01093799999944, + 80.96997499999998, + 82.67584199999692, + 85.84810600000492, + 87.5740650000007, + 83.22516899999755, + 86.76756100000057 + ], + "timingMs": { + "min": 80.96997499999998, + "median": 85.84810600000492, + "p95": 89.01093799999944, + "max": 89.01093799999944 + } + }, + "upstream": { + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 3, + -1 + ], + [ + 4, + -1 + ], + [ + 5, + 4 + ], + [ + 2, + -3 + ], + [ + 0, + -2 + ], + [ + 1, + -6 + ], + [ + 3, + -6 + ], + [ + 5, + 6 + ], + [ + 4, + -3 + ], + [ + 1, + -6 + ], + [ + 0, + -3 + ] + ], + "samplesMs": [ + 41.1922160000031, + 42.65492799999629, + 43.757456999999704, + 43.41924799999833, + 42.717342000003555, + 43.488172999997914, + 47.82670200000575 + ], + "timingMs": { + "min": 41.1922160000031, + "median": 43.41924799999833, + "p95": 47.82670200000575, + "max": 47.82670200000575 + } + }, + "candidate": { + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 3, + -2 + ], + [ + 5, + 4 + ], + [ + 0, + -1 + ], + [ + 1, + -6 + ], + [ + 0, + -4 + ], + [ + 3, + -2 + ], + [ + 1, + -6 + ], + [ + 4, + -4 + ], + [ + 5, + 6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ] + ], + "samplesMs": [ + 0.2305969999943045, + 0.21958100000483682, + 0.21743699999933597, + 0.23307999999815365, + 0.2544329999946058, + 0.21845899999607354, + 0.21779699999751756 + ], + "timingMs": { + "min": 0.21743699999933597, + "median": 0.21958100000483682, + "p95": 0.2544329999946058, + "max": 0.2544329999946058 + } + }, + "baseline": { + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 3, + -2 + ], + [ + 5, + 4 + ], + [ + 0, + -1 + ], + [ + 1, + -6 + ], + [ + 0, + -4 + ], + [ + 3, + -2 + ], + [ + 1, + -6 + ], + [ + 4, + -4 + ], + [ + 5, + 6 + ], + [ + 2, + -3 + ], + [ + 3, + -3 + ] + ], + "samplesMs": [ + 0.21152799999981653, + 0.2097959999955492, + 0.22449799999594688, + 0.21069799999531824, + 0.20967599999858066, + 0.21013599999423604, + 0.2737919999999576 + ], + "timingMs": { + "min": 0.20967599999858066, + "median": 0.21069799999531824, + "p95": 0.2737919999999576, + "max": 0.2737919999999576 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 1.0296320088791049, + "noiseRatio": 1.3819885810604369, + "lowerRatio": 0.8955646084687054, + "upperRatio": 1.1837695054979016 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0296320088791049, + "noiseRatio": 1.3819885810604369, + "lowerRatio": 0.8955646084687054, + "upperRatio": 1.1837695054979016, + "candidate": [ + 0.2305969999943045, + 0.21958100000483682, + 0.21743699999933597, + 0.23307999999815365, + 0.2544329999946058, + 0.21845899999607354, + 0.21779699999751756 + ], + "baseline": [ + 0.21152799999981653, + 0.2097959999955492, + 0.22449799999594688, + 0.21069799999531824, + 0.20967599999858066, + 0.21013599999423604, + 0.2737919999999576 + ], + "selfA": [ + 0.28309599999920465, + 0.12362600000051316, + 0.11836800000310177, + 0.13813700000173412, + 0.12466699999640696, + 0.1234859999967739, + 0.15292000000044936 + ], + "selfB": [ + 0.1598909999956959, + 0.13718599999992875, + 0.12060100000235252, + 0.15960099999938393, + 0.11916899999778252, + 0.11750700000266079, + 0.1355840000032913 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.1309262222565448, + "noiseRatio": 1.2598060574634062, + "lowerRatio": 0.9366055575541085, + "upperRatio": 1.3655632404397424, + "candidate": [ + 0.16683199999533826, + 0.18952600000193343, + 0.15767699999560136, + 0.1923700000043027, + 0.17130799999722512, + 0.18368599999666912, + 0.145368999998027 + ], + "baseline": [ + 0.16363600000477163, + 0.14726200000586687, + 0.15273899999738205, + 0.16516799999953946, + 0.17851800000062212, + 0.11621500000183005, + 0.14701100000092993 + ], + "selfA": [ + 0.15100699999311473, + 0.13251899999886518, + 0.15529399999650195, + 0.1612119999990682, + 0.1371759999965434, + 0.11929999999847496, + 0.1316679999945336 + ], + "selfB": [ + 0.12969499999599066, + 0.14862300000095274, + 0.13358100000186823, + 0.23630600000615232, + 0.12157300000399118, + 0.1295350000000326, + 0.13777700000355253 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.28309599999920465, + 0.12362600000051316, + 0.11836800000310177, + 0.13813700000173412, + 0.12466699999640696, + 0.1234859999967739, + 0.15292000000044936 + ], + "baselineSelfB": [ + 0.1598909999956959, + 0.13718599999992875, + 0.12060100000235252, + 0.15960099999938393, + 0.11916899999778252, + 0.11750700000266079, + 0.1355840000032913 + ] + }, + "historicalReference": { + "A": 11, + "U": 6, + "C": 41, + "plateSwitches": 10, + "commands": [ + [ + 3, + -1 + ], + [ + 4, + -1 + ], + [ + 5, + 4 + ], + [ + 2, + -3 + ], + [ + 0, + -2 + ], + [ + 1, + -6 + ], + [ + 3, + -6 + ], + [ + 5, + 6 + ], + [ + 4, + -3 + ], + [ + 1, + -6 + ], + [ + 0, + -3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + }, + { + "id": "old-to-new-camp-bandits-chest", + "title": "Chest by bandits on the path to the New Camp", + "optimumA": 10, + "solvers": { + "originalBfs": { + "A": 10, + "U": 5, + "C": 33, + "plateSwitches": 9, + "commands": [ + [ + 2, + -1 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 0, + 3 + ], + [ + 2, + -3 + ], + [ + 3, + 3 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ], + [ + 5, + -2 + ] + ], + "samplesMs": [ + 67.6412249999994, + 69.8904790000015, + 75.50874500000646, + 71.07624399999622, + 67.5849290000042, + 70.11189300000115, + 69.81533599999966 + ], + "timingMs": { + "min": 67.5849290000042, + "median": 69.8904790000015, + "p95": 75.50874500000646, + "max": 75.50874500000646 + } + }, + "upstream": { + "A": 10, + "U": 5, + "C": 33, + "plateSwitches": 9, + "commands": [ + [ + 5, + -2 + ], + [ + 4, + 6 + ], + [ + 2, + -1 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 0, + 3 + ], + [ + 4, + 6 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 27.31730399999651, + 27.873411000000488, + 27.836896000000706, + 28.83828299999732, + 30.98008700000355, + 27.19507000000158, + 29.070592999996734 + ], + "timingMs": { + "min": 27.19507000000158, + "median": 27.873411000000488, + "p95": 30.98008700000355, + "max": 30.98008700000355 + } + }, + "candidate": { + "A": 10, + "U": 5, + "C": 33, + "plateSwitches": 9, + "commands": [ + [ + 4, + 6 + ], + [ + 5, + -2 + ], + [ + 2, + -1 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 0.47354300000006333, + 0.3922909999964759, + 0.39261099999566795, + 0.5669229999984964, + 0.42150499999843305, + 0.47142900000471855, + 0.39204000000609085 + ], + "timingMs": { + "min": 0.39204000000609085, + "median": 0.42150499999843305, + "p95": 0.5669229999984964, + "max": 0.5669229999984964 + } + }, + "baseline": { + "A": 10, + "U": 5, + "C": 33, + "plateSwitches": 9, + "commands": [ + [ + 4, + 6 + ], + [ + 5, + -2 + ], + [ + 2, + -1 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + 6 + ], + [ + 0, + 3 + ] + ], + "samplesMs": [ + 0.42110399999364745, + 0.40192499999830034, + 0.4081649999934598, + 0.431099000001268, + 0.5397919999959413, + 0.5167270000019926, + 0.41836999999941327 + ], + "timingMs": { + "min": 0.40192499999830034, + "median": 0.42110399999364745, + "p95": 0.5397919999959413, + "max": 0.5397919999959413 + } + } + }, + "quality": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "timing": { + "verdict": "inconclusive", + "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", + "ratio": 0.9892063971650459, + "noiseRatio": 1.087938982953031, + "lowerRatio": 0.8317200041755886, + "upperRatio": 1.1765128784682548 + }, + "timingAttempts": [ + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 0.9892063971650459, + "noiseRatio": 1.087938982953031, + "lowerRatio": 0.8317200041755886, + "upperRatio": 1.1765128784682548, + "candidate": [ + 0.47354300000006333, + 0.3922909999964759, + 0.39261099999566795, + 0.5669229999984964, + 0.42150499999843305, + 0.47142900000471855, + 0.39204000000609085 + ], + "baseline": [ + 0.42110399999364745, + 0.40192499999830034, + 0.4081649999934598, + 0.431099000001268, + 0.5397919999959413, + 0.5167270000019926, + 0.41836999999941327 + ], + "selfA": [ + 0.32217499999387655, + 0.30742299999837996, + 0.3121800000008079, + 0.2949040000021341, + 0.2665310000011232, + 0.2485240000023623, + 0.30919499999436084 + ], + "selfB": [ + 0.3258999999961816, + 0.30911500000365777, + 0.2913780000017141, + 0.27406199999677483, + 0.25461299999733455, + 0.2568170000013197, + 0.28195399999822257 + ] + }, + { + "verdict": "inconclusive", + "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", + "ratio": 1.0073851085495418, + "noiseRatio": 1.5435618049946231, + "lowerRatio": 0.6733100683786263, + "upperRatio": 1.5072175578350326, + "candidate": [ + 0.6927930000019842, + 0.35863899999822024, + 0.34312599999975646, + 0.35830900000291876, + 0.36398799999733455, + 0.37846999999601394, + 0.41390300000057323 + ], + "baseline": [ + 0.36374800000339746, + 0.40292699999554316, + 0.6399129999990691, + 0.3501070000056643, + 0.4282939999975497, + 0.32939600000099745, + 0.35711799999990035 + ], + "selfA": [ + 0.3425150000039139, + 0.30326599999534665, + 0.34249499999714317, + 0.32473900000331923, + 0.3266619999994873, + 0.3286949999965145, + 0.35013700000126846 + ], + "selfB": [ + 0.3587100000004284, + 0.3275930000017979, + 0.3348939999996219, + 0.36730300000635907, + 0.4327919999996084, + 0.27856900000188034, + 0.6953160000048229 + ] + } + ], + "calibration": { + "baselineSelfA": [ + 0.32217499999387655, + 0.30742299999837996, + 0.3121800000008079, + 0.2949040000021341, + 0.2665310000011232, + 0.2485240000023623, + 0.30919499999436084 + ], + "baselineSelfB": [ + 0.3258999999961816, + 0.30911500000365777, + 0.2913780000017141, + 0.27406199999677483, + 0.25461299999733455, + 0.2568170000013197, + 0.28195399999822257 + ] + }, + "historicalReference": { + "A": 10, + "U": 5, + "C": 33, + "plateSwitches": 9, + "commands": [ + [ + 5, + -2 + ], + [ + 4, + 6 + ], + [ + 2, + -1 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ], + [ + 0, + 3 + ], + [ + 4, + 6 + ], + [ + 3, + 3 + ], + [ + 2, + -3 + ], + [ + 0, + 3 + ] + ], + "timingCompared": false + }, + "differences": { + "originalBfs": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "upstream": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + }, + "baseline": { + "quality": "passed", + "deltas": { + "A": 0, + "U": 0, + "C": 0, + "plateSwitches": 0 + }, + "regressions": [] + } + } + } + ], + "metadata": { + "environment": { + "node": "v24.19.0", + "v8": "13.6.233.17-node.51", + "platform": "linux", + "arch": "x64", + "cpu": "AMD EPYC 9V74 80-Core Processor", + "logicalCpus": 9, + "osRelease": "6.18.35", + "totalMemoryBytes": 23109910528, + "execArgv": [] + }, + "source": { + "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", + "dirty": false, + "files": { + "errors.ts": "830e0995bb1d5b98e8ce0495a22e5ae169cab10b2bdff2612e58323c397306f5", + "index.ts": "a1a2eedf5010cd66fc5d451035923e6ab417b36ba84cb2da85789cea17a1569a", + "indexed-heap.ts": "f776755f7405968b567922473c1e6ea5a63dae7643c4bd62b993159e900b7fbc", + "indexed.ts": "279b11d625a033f45723dd94df13a9be9fdfe267cff2535d5470b2011f11e125", + "lock-model.ts": "b4674921412e2acfbf0eaeff2031b3fc3907dffe886c42e588cd5ba6348ec966", + "matrix-analysis.ts": "a105dee650d645a730e3cc7e62a8e9faae31b29ad98c28aafce5ef15df236fc6", + "matrix-search.ts": "380ae745289a7ffd1997a6b9aa7dc3dd06a56e17453732d41042891c42435d3a", + "search-bfs.ts": "011c675973ccc8d753c0b9b3608882b16ca93ef4b460635c3474d8d5fc705feb", + "search-limits.ts": "91afc0940d49af46d9bb57d91af45df992f0684593d2f2d8bc3c13fad3f4c681", + "types.ts": "a2e72cf02e31694c52c1621cf5359863585b8383aa83a7d4e977c8547e8c879b" + }, + "harness": { + "catalog.ts": "2b75c9e9b5fe6997006307bf74cd90d8645d6d61fc527681f209c164680b880a", + "cli.ts": "b717c3d69c13d4a5b9a77589b1465f96338fe9d8be6501249f4470db035efe33", + "comparison.ts": "680ac76d266b51ff1f951fd3e232248bc5df0ab9eb70123285d31d176fb3a709", + "harness.ts": "14d23d697b2c4dd8adc13beac84b1cd3c0236aa34ae42ea96935fb30ac75e9f8", + "memory.ts": "429b45f541a2ac97e46fb6598ac30966cab4e2dca7e355c56f3ad3e037f43d93", + "rss.ts": "acc321bc34deac44819e5ff6d9af89e91c7394d9d897f7d69ce0cfedfc2e992c", + "solver.bench.ts": "985fbba3b387109261abeb1fdcbecd91b0ccfaa4dde5e0f162ad1758d529647f", + "validation.ts": "c44fc2a3de4da26ab207186a97a6b80ab20a910d27ae335da6cec536e402d77d" + }, + "scripts": { + "benchmark-compare.ts": "d4243c84a0a007b611e04e9c07031c3a7e48fb1a610366349084f0a7ab61cd71", + "benchmark-memory-worker.ts": "807707ca177f43c5ef08b88aa1068ad5f08f391b74f7f8d358159a2e5bf8a49b", + "benchmark-memory.ts": "c25a0407122f1c16261d8f8f9fd6f60cba463a7e288579ba67fde398db8d854f", + "benchmark-snapshot.ts": "50b31ffeac808f5b21f0fb864d2efd8a7c758491cf4e2d611bbe900bf3f9ec32", + "benchmark.ts": "fdb3f22dfdd495a6dde6a27fc3b0db68c7ceb75b94acaff131d30a25b6a06f24" + } + }, + "candidate": { + "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", + "moduleSha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b", + "format": "tuple", + "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/dist/gothic-lock-solver.mjs" + }, + "baseline": { + "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", + "moduleSha256": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "format": "legacy", + "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/reference-matrix/src/index.mjs", + "moduleTree": { + "cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", + "index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", + "matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", + "matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", + "result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", + "search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", + "search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", + "solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", + "state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", + "transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" + }, + "snapshotSha256": "c21b6319523f625439403c0417d267b5a63c35abafebef758bfc6c21217a3b94", + "snapshot": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/snapshots/reference-matrix.json", + "kind": "reference-baseline" + }, + "fixtures": { + "count": 45, + "catalogSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", + "manifestSha256": "a634d5328fd8d41470397b622ae0bd32b8aac0355dfc31975e023a63a62e65c9" + }, + "originalBfs": { + "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", + "revision": "a87e739a22ccc4215d6d9b14fa06b0738171cb60", + "license": "Apache-2.0", + "files": { + "src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", + "src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", + "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", + "src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", + "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", + "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", + "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" + }, + "notes": "Unmodified source snapshots; imports remain relative within this directory. No upstream UnlockMyLoot solver code is included." + }, + "matrixReference": { + "schemaVersion": 1, + "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", + "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", + "license": "Apache-2.0", + "files": { + "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "src/cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", + "src/index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "src/lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", + "src/matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", + "src/matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", + "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", + "src/search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", + "src/search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", + "src/solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", + "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", + "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" + }, + "notes": "Byte-identical Apache matrix reference, copied from source at the pinned revision. Benchmark-only; excluded from runtime and npm package. No external AGPL solver source." + }, + "upstream": { + "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", + "solver": { + "path": "index.html", + "sha256": "691f744099a9169f30e9883fddc5fdbb4949d306f3011e601678419d7b93821b", + "blockSha256": "d5bc85f40fd7c5b61097403df399ecb7dfd2b58924dc7a670aa0d05306017380", + "startMarker": "/* solver-start", + "endMarker": "/* solver-end */" + }, + "measured": true, + "externalCheckout": "/workspace/scratch/5d94b8672fa4/unlockmyloot", + "license": "External AGPL source is hash-verified and executed only from the supplied pinned checkout; no upstream solver code is vendored." + }, + "artifacts": { + "gothic-lock-solver.mjs": { + "bytes": 20143, + "sha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b" + }, + "gothic-lock-solver.min.mjs": { + "bytes": 9098, + "sha256": "9a7c0d365a0e2630372200f4c8a7914024b04afd048ca77e6a6783c120676d1f" + }, + "gothic-lock-solver.js": { + "bytes": 20916, + "sha256": "14841c4bc1563f4fd8beae3e277798878a2862efa0840e50b1aef9d5e5dff101" + }, + "gothic-lock-solver.min.js": { + "bytes": 9197, + "sha256": "2a936f190ae38f458e2b209681beb099974f32d654eb33be7249be6ede847694" + }, + "gothic-lock-solver.cli.mjs": { + "bytes": 23199, + "sha256": "75129dff219af243b05ffcbdb63ac1a14a32fd0d0b5ae8ee4528bb4d24b75f81" + } + } + }, + "memory": { + "metric": "Linux /proc/self/status VmHWM × 1024 bytes (current process-image RSS high-water)", + "repetitions": 5, + "sources": [ + "https://docs.kernel.org/filesystems/proc.html", + "https://nodejs.org/api/process.html#processresourceusage" + ], + "accuracy": "Kernel RSS accounting is asynchronous and approximate; this is not an exact allocation count. Raw getrusage maxRSS and startup/current-image peaks are preserved to expose pre-exec launch floors.", + "scope": "Each per-lock observation is a fresh process. Repeated comparison is peak RSS of the selected 45-lock workload per fresh process.", + "includes": "Node runtime, TypeScript benchmark loader, module loading, fixture loading, normalization, independent replay and solver allocations. Startup and loaded current RSS are recorded separately; subtraction is not exact algorithm allocation.", + "browser": { + "available": false, + "reason": "No portable exact browser peak-memory API; Node RSS is not browser memory." + }, + "performance": { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.962246756993597, + "noiseRatio": 1.0949647700001646, + "lowerRatio": 0.8908822461111241, + "upperRatio": 1.0393279531458977 + }, + "attempts": [ + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.962246756993597, + "noiseRatio": 1.0949647700001646, + "lowerRatio": 0.8908822461111241, + "upperRatio": 1.0393279531458977, + "samples": { + "candidate": [ + { + "peakRssBytes": 94666752, + "startupRssBytes": 64344064, + "loadedRssBytes": 65785856, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65351680, + "loadedPeakRssBytes": 66846720, + "resourceUsageMaxRssBytes": 93732864, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94412800, + "startupRssBytes": 65175552, + "loadedRssBytes": 66617344, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66248704, + "loadedPeakRssBytes": 67477504, + "resourceUsageMaxRssBytes": 93741056, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94752768, + "startupRssBytes": 65814528, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66715648, + "loadedPeakRssBytes": 67567616, + "resourceUsageMaxRssBytes": 94523392, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89186304, + "startupRssBytes": 65310720, + "loadedRssBytes": 66883584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66576384, + "loadedPeakRssBytes": 67739648, + "resourceUsageMaxRssBytes": 89006080, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84447232, + "startupRssBytes": 65482752, + "loadedRssBytes": 67186688, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66744320, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "baseline": [ + { + "peakRssBytes": 95948800, + "startupRssBytes": 64970752, + "loadedRssBytes": 67461120, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66179072, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 95100928, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89391104, + "startupRssBytes": 64778240, + "loadedRssBytes": 67137536, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65925120, + "loadedPeakRssBytes": 68210688, + "resourceUsageMaxRssBytes": 88641536, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 100007936, + "startupRssBytes": 64532480, + "loadedRssBytes": 67678208, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65564672, + "loadedPeakRssBytes": 68284416, + "resourceUsageMaxRssBytes": 100007936, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95895552, + "startupRssBytes": 65687552, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67055616, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 95895552, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 93995008, + "startupRssBytes": 65671168, + "loadedRssBytes": 67375104, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66916352, + "loadedPeakRssBytes": 68136960, + "resourceUsageMaxRssBytes": 93188096, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "originalBfs": [ + { + "peakRssBytes": 121143296, + "startupRssBytes": 65421312, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66490368, + "loadedPeakRssBytes": 67485696, + "resourceUsageMaxRssBytes": 120414208, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120922112, + "startupRssBytes": 65372160, + "loadedRssBytes": 67207168, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 120160256, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120438784, + "startupRssBytes": 64557056, + "loadedRssBytes": 66392064, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65339392, + "loadedPeakRssBytes": 67448832, + "resourceUsageMaxRssBytes": 119697408, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120311808, + "startupRssBytes": 65306624, + "loadedRssBytes": 67141632, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66830336, + "loadedPeakRssBytes": 68071424, + "resourceUsageMaxRssBytes": 119595008, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 121356288, + "startupRssBytes": 65691648, + "loadedRssBytes": 67395584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 68116480, + "resourceUsageMaxRssBytes": 120606720, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "upstream": [ + { + "peakRssBytes": 270356480, + "startupRssBytes": 65941504, + "loadedRssBytes": 68431872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67108864, + "loadedPeakRssBytes": 69251072, + "resourceUsageMaxRssBytes": 269815808, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270110720, + "startupRssBytes": 65540096, + "loadedRssBytes": 68685824, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 69443584, + "resourceUsageMaxRssBytes": 269246464, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 268992512, + "startupRssBytes": 64909312, + "loadedRssBytes": 68710400, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65966080, + "loadedPeakRssBytes": 69349376, + "resourceUsageMaxRssBytes": 267882496, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 309043200, + "startupRssBytes": 65306624, + "loadedRssBytes": 68452352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66768896, + "loadedPeakRssBytes": 69234688, + "resourceUsageMaxRssBytes": 309043200, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270135296, + "startupRssBytes": 65089536, + "loadedRssBytes": 68497408, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65785856, + "loadedPeakRssBytes": 69300224, + "resourceUsageMaxRssBytes": 269443072, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "baselineSelfA": [ + { + "peakRssBytes": 95563776, + "startupRssBytes": 65470464, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66633728, + "loadedPeakRssBytes": 68255744, + "resourceUsageMaxRssBytes": 95563776, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95862784, + "startupRssBytes": 65462272, + "loadedRssBytes": 67297280, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66928640, + "loadedPeakRssBytes": 68186112, + "resourceUsageMaxRssBytes": 95862784, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94965760, + "startupRssBytes": 65818624, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67014656, + "loadedPeakRssBytes": 68362240, + "resourceUsageMaxRssBytes": 94965760, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95129600, + "startupRssBytes": 65208320, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66326528, + "loadedPeakRssBytes": 68149248, + "resourceUsageMaxRssBytes": 94162944, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94371840, + "startupRssBytes": 64974848, + "loadedRssBytes": 66940928, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66097152, + "loadedPeakRssBytes": 67719168, + "resourceUsageMaxRssBytes": 94371840, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "baselineSelfB": [ + { + "peakRssBytes": 96808960, + "startupRssBytes": 64933888, + "loadedRssBytes": 67424256, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66068480, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 96731136, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94896128, + "startupRssBytes": 65703936, + "loadedRssBytes": 67407872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 94896128, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84250624, + "startupRssBytes": 65826816, + "loadedRssBytes": 67530752, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66949120, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 97107968, + "startupRssBytes": 65032192, + "loadedRssBytes": 66998272, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66183168, + "loadedPeakRssBytes": 67727360, + "resourceUsageMaxRssBytes": 97107968, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94167040, + "startupRssBytes": 64937984, + "loadedRssBytes": 67428352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66555904, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 93442048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ] + } + } + ], + "wholeCatalog": { + "candidate": { + "samples": [ + { + "peakRssBytes": 94666752, + "startupRssBytes": 64344064, + "loadedRssBytes": 65785856, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65351680, + "loadedPeakRssBytes": 66846720, + "resourceUsageMaxRssBytes": 93732864, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94412800, + "startupRssBytes": 65175552, + "loadedRssBytes": 66617344, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66248704, + "loadedPeakRssBytes": 67477504, + "resourceUsageMaxRssBytes": 93741056, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94752768, + "startupRssBytes": 65814528, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66715648, + "loadedPeakRssBytes": 67567616, + "resourceUsageMaxRssBytes": 94523392, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89186304, + "startupRssBytes": 65310720, + "loadedRssBytes": 66883584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66576384, + "loadedPeakRssBytes": 67739648, + "resourceUsageMaxRssBytes": 89006080, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84447232, + "startupRssBytes": 65482752, + "loadedRssBytes": 67186688, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66744320, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 84447232, + "median": 94412800, + "p95": 94752768, + "max": 94752768 + } + }, + "baseline": { + "samples": [ + { + "peakRssBytes": 95948800, + "startupRssBytes": 64970752, + "loadedRssBytes": 67461120, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66179072, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 95100928, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89391104, + "startupRssBytes": 64778240, + "loadedRssBytes": 67137536, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65925120, + "loadedPeakRssBytes": 68210688, + "resourceUsageMaxRssBytes": 88641536, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 100007936, + "startupRssBytes": 64532480, + "loadedRssBytes": 67678208, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65564672, + "loadedPeakRssBytes": 68284416, + "resourceUsageMaxRssBytes": 100007936, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95895552, + "startupRssBytes": 65687552, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67055616, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 95895552, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 93995008, + "startupRssBytes": 65671168, + "loadedRssBytes": 67375104, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66916352, + "loadedPeakRssBytes": 68136960, + "resourceUsageMaxRssBytes": 93188096, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 89391104, + "median": 95895552, + "p95": 100007936, + "max": 100007936 + } + }, + "originalBfs": { + "samples": [ + { + "peakRssBytes": 121143296, + "startupRssBytes": 65421312, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66490368, + "loadedPeakRssBytes": 67485696, + "resourceUsageMaxRssBytes": 120414208, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120922112, + "startupRssBytes": 65372160, + "loadedRssBytes": 67207168, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 120160256, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120438784, + "startupRssBytes": 64557056, + "loadedRssBytes": 66392064, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65339392, + "loadedPeakRssBytes": 67448832, + "resourceUsageMaxRssBytes": 119697408, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120311808, + "startupRssBytes": 65306624, + "loadedRssBytes": 67141632, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66830336, + "loadedPeakRssBytes": 68071424, + "resourceUsageMaxRssBytes": 119595008, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 121356288, + "startupRssBytes": 65691648, + "loadedRssBytes": 67395584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 68116480, + "resourceUsageMaxRssBytes": 120606720, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 120311808, + "median": 120922112, + "p95": 121356288, + "max": 121356288 + } + }, + "upstream": { + "samples": [ + { + "peakRssBytes": 270356480, + "startupRssBytes": 65941504, + "loadedRssBytes": 68431872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67108864, + "loadedPeakRssBytes": 69251072, + "resourceUsageMaxRssBytes": 269815808, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270110720, + "startupRssBytes": 65540096, + "loadedRssBytes": 68685824, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 69443584, + "resourceUsageMaxRssBytes": 269246464, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 268992512, + "startupRssBytes": 64909312, + "loadedRssBytes": 68710400, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65966080, + "loadedPeakRssBytes": 69349376, + "resourceUsageMaxRssBytes": 267882496, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 309043200, + "startupRssBytes": 65306624, + "loadedRssBytes": 68452352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66768896, + "loadedPeakRssBytes": 69234688, + "resourceUsageMaxRssBytes": 309043200, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270135296, + "startupRssBytes": 65089536, + "loadedRssBytes": 68497408, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65785856, + "loadedPeakRssBytes": 69300224, + "resourceUsageMaxRssBytes": 269443072, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 268992512, + "median": 270135296, + "p95": 309043200, + "max": 309043200 + } + }, + "baselineSelfA": { + "samples": [ + { + "peakRssBytes": 95563776, + "startupRssBytes": 65470464, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66633728, + "loadedPeakRssBytes": 68255744, + "resourceUsageMaxRssBytes": 95563776, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95862784, + "startupRssBytes": 65462272, + "loadedRssBytes": 67297280, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66928640, + "loadedPeakRssBytes": 68186112, + "resourceUsageMaxRssBytes": 95862784, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94965760, + "startupRssBytes": 65818624, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67014656, + "loadedPeakRssBytes": 68362240, + "resourceUsageMaxRssBytes": 94965760, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95129600, + "startupRssBytes": 65208320, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66326528, + "loadedPeakRssBytes": 68149248, + "resourceUsageMaxRssBytes": 94162944, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94371840, + "startupRssBytes": 64974848, + "loadedRssBytes": 66940928, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66097152, + "loadedPeakRssBytes": 67719168, + "resourceUsageMaxRssBytes": 94371840, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 94371840, + "median": 95129600, + "p95": 95862784, + "max": 95862784 + } + }, + "baselineSelfB": { + "samples": [ + { + "peakRssBytes": 96808960, + "startupRssBytes": 64933888, + "loadedRssBytes": 67424256, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66068480, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 96731136, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94896128, + "startupRssBytes": 65703936, + "loadedRssBytes": 67407872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 94896128, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84250624, + "startupRssBytes": 65826816, + "loadedRssBytes": 67530752, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66949120, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 97107968, + "startupRssBytes": 65032192, + "loadedRssBytes": 66998272, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66183168, + "loadedPeakRssBytes": 67727360, + "resourceUsageMaxRssBytes": 97107968, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94167040, + "startupRssBytes": 64937984, + "loadedRssBytes": 67428352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66555904, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 93442048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 84250624, + "median": 94896128, + "p95": 97107968, + "max": 97107968 + } + } + }, + "perLock": [ + { + "id": "ruined-tower-door", + "solvers": { + "candidate": { + "peakRssBytes": 77537280, + "startupRssBytes": 65085440, + "loadedRssBytes": 66658304, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66162688, + "loadedPeakRssBytes": 67620864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 74416128, + "startupRssBytes": 64368640, + "loadedRssBytes": 67383296, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65441792, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78462976, + "startupRssBytes": 65814528, + "loadedRssBytes": 67125248, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66789376, + "loadedPeakRssBytes": 67813376, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 106233856, + "startupRssBytes": 64303104, + "loadedRssBytes": 68497408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65495040, + "loadedPeakRssBytes": 69402624, + "resourceUsageMaxRssBytes": 105652224, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "scatty-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71573504, + "startupRssBytes": 65675264, + "loadedRssBytes": 67117056, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66748416, + "loadedPeakRssBytes": 67698688, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71282688, + "startupRssBytes": 65290240, + "loadedRssBytes": 67387392, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66768896, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78876672, + "startupRssBytes": 66211840, + "loadedRssBytes": 67260416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67403776, + "loadedPeakRssBytes": 68096000, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75640832, + "startupRssBytes": 65265664, + "loadedRssBytes": 68411392, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66207744, + "loadedPeakRssBytes": 69148672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "cor-galom-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72212480, + "startupRssBytes": 65150976, + "loadedRssBytes": 66592768, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66125824, + "loadedPeakRssBytes": 67354624, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71266304, + "startupRssBytes": 65617920, + "loadedRssBytes": 67452928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66957312, + "loadedPeakRssBytes": 68227072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 76562432, + "startupRssBytes": 65687552, + "loadedRssBytes": 67391488, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66940928, + "loadedPeakRssBytes": 68087808, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 73269248, + "startupRssBytes": 65531904, + "loadedRssBytes": 68415488, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67010560, + "loadedPeakRssBytes": 69345280, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "cor-galom-bedroom-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72265728, + "startupRssBytes": 65675264, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67072000, + "loadedPeakRssBytes": 68050944, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71311360, + "startupRssBytes": 65495040, + "loadedRssBytes": 67461120, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 68280320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78819328, + "startupRssBytes": 65101824, + "loadedRssBytes": 67198976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66678784, + "loadedPeakRssBytes": 68079616, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78999552, + "startupRssBytes": 65703936, + "loadedRssBytes": 68456448, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66990080, + "loadedPeakRssBytes": 69292032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "swamp-camp-forge-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72208384, + "startupRssBytes": 65228800, + "loadedRssBytes": 66670592, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66412544, + "loadedPeakRssBytes": 67661824, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71602176, + "startupRssBytes": 64950272, + "loadedRssBytes": 67571712, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65806336, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78815232, + "startupRssBytes": 65097728, + "loadedRssBytes": 67325952, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66359296, + "loadedPeakRssBytes": 68022272, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 83496960, + "startupRssBytes": 65830912, + "loadedRssBytes": 68714496, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67059712, + "loadedPeakRssBytes": 69357568, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "rice-lord-storeroom", + "solvers": { + "candidate": { + "peakRssBytes": 71999488, + "startupRssBytes": 65429504, + "loadedRssBytes": 66740224, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66564096, + "loadedPeakRssBytes": 67616768, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71303168, + "startupRssBytes": 65781760, + "loadedRssBytes": 67747840, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67104768, + "loadedPeakRssBytes": 68378624, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78467072, + "startupRssBytes": 65544192, + "loadedRssBytes": 66985984, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66797568, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75665408, + "startupRssBytes": 65040384, + "loadedRssBytes": 68448256, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65794048, + "loadedPeakRssBytes": 69210112, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-chambers-door", + "solvers": { + "candidate": { + "peakRssBytes": 72138752, + "startupRssBytes": 65363968, + "loadedRssBytes": 66936832, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66924544, + "loadedPeakRssBytes": 67854336, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71692288, + "startupRssBytes": 65499136, + "loadedRssBytes": 67203072, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66719744, + "loadedPeakRssBytes": 67993600, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79171584, + "startupRssBytes": 65282048, + "loadedRssBytes": 67248128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66682880, + "loadedPeakRssBytes": 68141056, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 80289792, + "startupRssBytes": 65380352, + "loadedRssBytes": 68395008, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67026944, + "loadedPeakRssBytes": 69320704, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-throne-hall-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71208960, + "startupRssBytes": 65286144, + "loadedRssBytes": 66727936, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66678784, + "loadedPeakRssBytes": 67715072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71991296, + "startupRssBytes": 65724416, + "loadedRssBytes": 67297280, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67112960, + "loadedPeakRssBytes": 68227072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 72019968, + "startupRssBytes": 65179648, + "loadedRssBytes": 67801088, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66400256, + "loadedPeakRssBytes": 68497408, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 72011776, + "startupRssBytes": 64962560, + "loadedRssBytes": 68632576, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66564096, + "loadedPeakRssBytes": 69357568, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "old-camp-dungeon-door", + "solvers": { + "candidate": { + "peakRssBytes": 77049856, + "startupRssBytes": 65257472, + "loadedRssBytes": 67092480, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66510848, + "loadedPeakRssBytes": 67887104, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 75661312, + "startupRssBytes": 65220608, + "loadedRssBytes": 67448832, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66314240, + "loadedPeakRssBytes": 68214784, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79208448, + "startupRssBytes": 63983616, + "loadedRssBytes": 66473984, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65093632, + "loadedPeakRssBytes": 67276800, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 85356544, + "startupRssBytes": 65171456, + "loadedRssBytes": 68448256, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66473984, + "loadedPeakRssBytes": 69226496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arlin-dungeon-key", + "solvers": { + "candidate": { + "peakRssBytes": 81268736, + "startupRssBytes": 65007616, + "loadedRssBytes": 66449408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66531328, + "loadedPeakRssBytes": 67469312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 79904768, + "startupRssBytes": 65724416, + "loadedRssBytes": 67428352, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66981888, + "loadedPeakRssBytes": 68276224, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 83525632, + "startupRssBytes": 64585728, + "loadedRssBytes": 67338240, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65556480, + "loadedPeakRssBytes": 68038656, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 76693504, + "startupRssBytes": 65495040, + "loadedRssBytes": 68509696, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66912256, + "loadedPeakRssBytes": 69320704, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-wine-cellar", + "solvers": { + "candidate": { + "peakRssBytes": 71606272, + "startupRssBytes": 65417216, + "loadedRssBytes": 66859008, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66609152, + "loadedPeakRssBytes": 67608576, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71225344, + "startupRssBytes": 65245184, + "loadedRssBytes": 67473408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66383872, + "loadedPeakRssBytes": 68194304, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79204352, + "startupRssBytes": 65544192, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66928640, + "loadedPeakRssBytes": 68059136, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75743232, + "startupRssBytes": 65306624, + "loadedRssBytes": 68583424, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 69271552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedside-chest", + "solvers": { + "candidate": { + "peakRssBytes": 76054528, + "startupRssBytes": 65183744, + "loadedRssBytes": 66887680, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66506752, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73519104, + "startupRssBytes": 64593920, + "loadedRssBytes": 67215360, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65310720, + "loadedPeakRssBytes": 67936256, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78987264, + "startupRssBytes": 64577536, + "loadedRssBytes": 67198976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65683456, + "loadedPeakRssBytes": 67956736, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78557184, + "startupRssBytes": 65847296, + "loadedRssBytes": 68599808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67043328, + "loadedPeakRssBytes": 69226496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-wall-chest", + "solvers": { + "candidate": { + "peakRssBytes": 79142912, + "startupRssBytes": 65122304, + "loadedRssBytes": 66957312, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66228224, + "loadedPeakRssBytes": 67846144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 76414976, + "startupRssBytes": 64720896, + "loadedRssBytes": 67080192, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65634304, + "loadedPeakRssBytes": 67944448, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79220736, + "startupRssBytes": 65282048, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66985984, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 111869952, + "startupRssBytes": 64249856, + "loadedRssBytes": 68444160, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65085440, + "loadedPeakRssBytes": 69144576, + "resourceUsageMaxRssBytes": 111263744, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "voran-bedside-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71966720, + "startupRssBytes": 65683456, + "loadedRssBytes": 66994176, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67055616, + "loadedPeakRssBytes": 67956736, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71983104, + "startupRssBytes": 65404928, + "loadedRssBytes": 67502080, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66428928, + "loadedPeakRssBytes": 68198400, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 83914752, + "startupRssBytes": 65208320, + "loadedRssBytes": 67436544, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66514944, + "loadedPeakRssBytes": 68112384, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 123584512, + "startupRssBytes": 65044480, + "loadedRssBytes": 68190208, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66375680, + "loadedPeakRssBytes": 69156864, + "resourceUsageMaxRssBytes": 122744832, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arto-room-door", + "solvers": { + "candidate": { + "peakRssBytes": 71913472, + "startupRssBytes": 65241088, + "loadedRssBytes": 66551808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66400256, + "loadedPeakRssBytes": 67444736, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70885376, + "startupRssBytes": 65437696, + "loadedRssBytes": 67272704, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66719744, + "loadedPeakRssBytes": 67952640, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78823424, + "startupRssBytes": 65675264, + "loadedRssBytes": 67248128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67026944, + "loadedPeakRssBytes": 68091904, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74354688, + "startupRssBytes": 65679360, + "loadedRssBytes": 68038656, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66564096, + "loadedPeakRssBytes": 68825088, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arto-far-bed-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72302592, + "startupRssBytes": 65015808, + "loadedRssBytes": 66326528, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66314240, + "loadedPeakRssBytes": 67268608, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71376896, + "startupRssBytes": 65216512, + "loadedRssBytes": 67575808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66478080, + "loadedPeakRssBytes": 68255744, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78929920, + "startupRssBytes": 65646592, + "loadedRssBytes": 67219456, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66936832, + "loadedPeakRssBytes": 68046848, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74788864, + "startupRssBytes": 65531904, + "loadedRssBytes": 68546560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66867200, + "loadedPeakRssBytes": 69308416, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arto-near-bed-chest", + "solvers": { + "candidate": { + "peakRssBytes": 73064448, + "startupRssBytes": 64270336, + "loadedRssBytes": 65974272, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65445888, + "loadedPeakRssBytes": 66961408, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71794688, + "startupRssBytes": 64516096, + "loadedRssBytes": 66088960, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65302528, + "loadedPeakRssBytes": 66691072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78839808, + "startupRssBytes": 65679360, + "loadedRssBytes": 67383296, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66977792, + "loadedPeakRssBytes": 68071424, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 110370816, + "startupRssBytes": 64880640, + "loadedRssBytes": 68419584, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66088960, + "loadedPeakRssBytes": 69353472, + "resourceUsageMaxRssBytes": 109670400, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedrooms-door", + "solvers": { + "candidate": { + "peakRssBytes": 79302656, + "startupRssBytes": 65355776, + "loadedRssBytes": 66928640, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66699264, + "loadedPeakRssBytes": 67833856, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 78962688, + "startupRssBytes": 65097728, + "loadedRssBytes": 67325952, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66465792, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79081472, + "startupRssBytes": 64839680, + "loadedRssBytes": 67067904, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65900544, + "loadedPeakRssBytes": 67969024, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 84283392, + "startupRssBytes": 64897024, + "loadedRssBytes": 68698112, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65888256, + "loadedPeakRssBytes": 69361664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedrooms-far-chest", + "solvers": { + "candidate": { + "peakRssBytes": 75325440, + "startupRssBytes": 65163264, + "loadedRssBytes": 66867200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66473984, + "loadedPeakRssBytes": 67735552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73637888, + "startupRssBytes": 65191936, + "loadedRssBytes": 67420160, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66863104, + "loadedPeakRssBytes": 68190208, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79294464, + "startupRssBytes": 67203072, + "loadedRssBytes": 67596288, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67887104, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75583488, + "startupRssBytes": 65593344, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66969600, + "loadedPeakRssBytes": 69156864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedrooms-near-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71790592, + "startupRssBytes": 64991232, + "loadedRssBytes": 66301952, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65740800, + "loadedPeakRssBytes": 67002368, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71270400, + "startupRssBytes": 64344064, + "loadedRssBytes": 67489792, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65359872, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79159296, + "startupRssBytes": 64802816, + "loadedRssBytes": 67293184, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66056192, + "loadedPeakRssBytes": 68067328, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 85274624, + "startupRssBytes": 65421312, + "loadedRssBytes": 68435968, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 69005312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-left-chest", + "solvers": { + "candidate": { + "peakRssBytes": 75079680, + "startupRssBytes": 65523712, + "loadedRssBytes": 66965504, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66568192, + "loadedPeakRssBytes": 67747840, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73498624, + "startupRssBytes": 65802240, + "loadedRssBytes": 67244032, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66772992, + "loadedPeakRssBytes": 67981312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79056896, + "startupRssBytes": 64958464, + "loadedRssBytes": 67186688, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65679360, + "loadedPeakRssBytes": 68022272, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 92065792, + "startupRssBytes": 64827392, + "loadedRssBytes": 68497408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66138112, + "loadedPeakRssBytes": 69206016, + "resourceUsageMaxRssBytes": 91205632, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-right-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72089600, + "startupRssBytes": 65638400, + "loadedRssBytes": 67211264, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67026944, + "loadedPeakRssBytes": 67960832, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72290304, + "startupRssBytes": 66674688, + "loadedRssBytes": 67592192, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67465216, + "loadedPeakRssBytes": 68386816, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79151104, + "startupRssBytes": 64765952, + "loadedRssBytes": 67256320, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65757184, + "loadedPeakRssBytes": 68038656, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 76038144, + "startupRssBytes": 65339392, + "loadedRssBytes": 68354048, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66514944, + "loadedPeakRssBytes": 69271552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor2-stairs-chest", + "solvers": { + "candidate": { + "peakRssBytes": 78045184, + "startupRssBytes": 64905216, + "loadedRssBytes": 66740224, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66195456, + "loadedPeakRssBytes": 67358720, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 75186176, + "startupRssBytes": 64901120, + "loadedRssBytes": 67260416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66289664, + "loadedPeakRssBytes": 68206592, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78721024, + "startupRssBytes": 64835584, + "loadedRssBytes": 67063808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66416640, + "loadedPeakRssBytes": 67735552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 93245440, + "startupRssBytes": 65855488, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66990080, + "loadedPeakRssBytes": 69308416, + "resourceUsageMaxRssBytes": 92598272, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor2-chest-2", + "solvers": { + "candidate": { + "peakRssBytes": 72044544, + "startupRssBytes": 65884160, + "loadedRssBytes": 67063808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67031040, + "loadedPeakRssBytes": 67891200, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72298496, + "startupRssBytes": 65290240, + "loadedRssBytes": 67256320, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66572288, + "loadedPeakRssBytes": 68222976, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78888960, + "startupRssBytes": 65081344, + "loadedRssBytes": 67178496, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66191360, + "loadedPeakRssBytes": 68038656, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74493952, + "startupRssBytes": 64372736, + "loadedRssBytes": 68567040, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65331200, + "loadedPeakRssBytes": 69373952, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor3-entrance-chest", + "solvers": { + "candidate": { + "peakRssBytes": 73695232, + "startupRssBytes": 65236992, + "loadedRssBytes": 66547712, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66519040, + "loadedPeakRssBytes": 67457024, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73441280, + "startupRssBytes": 65343488, + "loadedRssBytes": 67702784, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66543616, + "loadedPeakRssBytes": 68182016, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78643200, + "startupRssBytes": 65867776, + "loadedRssBytes": 67047424, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66863104, + "loadedPeakRssBytes": 67829760, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 76640256, + "startupRssBytes": 65904640, + "loadedRssBytes": 68657152, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66973696, + "loadedPeakRssBytes": 69349376, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor3-empty-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71852032, + "startupRssBytes": 66080768, + "loadedRssBytes": 66998272, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67137536, + "loadedPeakRssBytes": 67940352, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71245824, + "startupRssBytes": 65163264, + "loadedRssBytes": 67260416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66428928, + "loadedPeakRssBytes": 68218880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78536704, + "startupRssBytes": 65486848, + "loadedRssBytes": 66928640, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66781184, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75935744, + "startupRssBytes": 64512000, + "loadedRssBytes": 68575232, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65622016, + "loadedPeakRssBytes": 69304320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor4-elixirs-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72155136, + "startupRssBytes": 64491520, + "loadedRssBytes": 66719744, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65703936, + "loadedPeakRssBytes": 67670016, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71299072, + "startupRssBytes": 65552384, + "loadedRssBytes": 67387392, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 82427904, + "startupRssBytes": 65687552, + "loadedRssBytes": 66736128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66732032, + "loadedPeakRssBytes": 67620864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78761984, + "startupRssBytes": 65454080, + "loadedRssBytes": 68730880, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 69373952, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor4-food-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72327168, + "startupRssBytes": 64372736, + "loadedRssBytes": 65421312, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65400832, + "loadedPeakRssBytes": 66535424, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71421952, + "startupRssBytes": 65310720, + "loadedRssBytes": 67407872, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66527232, + "loadedPeakRssBytes": 68280320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79224832, + "startupRssBytes": 65339392, + "loadedRssBytes": 67174400, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66560000, + "loadedPeakRssBytes": 68120576, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 93716480, + "startupRssBytes": 65572864, + "loadedRssBytes": 67801088, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66535424, + "loadedPeakRssBytes": 68571136, + "resourceUsageMaxRssBytes": 93716480, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-top-chest", + "solvers": { + "candidate": { + "peakRssBytes": 79269888, + "startupRssBytes": 66072576, + "loadedRssBytes": 67121152, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67076096, + "loadedPeakRssBytes": 67948544, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 76902400, + "startupRssBytes": 65351680, + "loadedRssBytes": 67710976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66949120, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78548992, + "startupRssBytes": 65146880, + "loadedRssBytes": 66981888, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 67817472, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 86102016, + "startupRssBytes": 65794048, + "loadedRssBytes": 68546560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66965504, + "loadedPeakRssBytes": 69246976, + "resourceUsageMaxRssBytes": 85250048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "fire-lizard-cave-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71999488, + "startupRssBytes": 65421312, + "loadedRssBytes": 67125248, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66793472, + "loadedPeakRssBytes": 67944448, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71417856, + "startupRssBytes": 64843776, + "loadedRssBytes": 67334144, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66064384, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79163392, + "startupRssBytes": 65540096, + "loadedRssBytes": 66850816, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66707456, + "loadedPeakRssBytes": 67694592, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 94773248, + "startupRssBytes": 65036288, + "loadedRssBytes": 68706304, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66277376, + "loadedPeakRssBytes": 69341184, + "resourceUsageMaxRssBytes": 94773248, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "silas-tavern-basement-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72122368, + "startupRssBytes": 64565248, + "loadedRssBytes": 66269184, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65400832, + "loadedPeakRssBytes": 66957312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71405568, + "startupRssBytes": 65630208, + "loadedRssBytes": 67727360, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66318336, + "loadedPeakRssBytes": 68210688, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78974976, + "startupRssBytes": 65765376, + "loadedRssBytes": 67600384, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67031040, + "loadedPeakRssBytes": 68091904, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74752000, + "startupRssBytes": 65855488, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67010560, + "loadedPeakRssBytes": 69316608, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "silas-tavern-storeroom-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71946240, + "startupRssBytes": 66117632, + "loadedRssBytes": 67166208, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67235840, + "loadedPeakRssBytes": 67985408, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71139328, + "startupRssBytes": 65794048, + "loadedRssBytes": 67760128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67022848, + "loadedPeakRssBytes": 68370432, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79196160, + "startupRssBytes": 65155072, + "loadedRssBytes": 67252224, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66383872, + "loadedPeakRssBytes": 68141056, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78635008, + "startupRssBytes": 64790528, + "loadedRssBytes": 68329472, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66011136, + "loadedPeakRssBytes": 69222400, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "silas-tavern-storeroom-door", + "solvers": { + "candidate": { + "peakRssBytes": 72241152, + "startupRssBytes": 64372736, + "loadedRssBytes": 65290240, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65490944, + "loadedPeakRssBytes": 66146304, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71512064, + "startupRssBytes": 64557056, + "loadedRssBytes": 67440640, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65273856, + "loadedPeakRssBytes": 68132864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78893056, + "startupRssBytes": 65040384, + "loadedRssBytes": 67399680, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66592768, + "loadedPeakRssBytes": 68124672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75194368, + "startupRssBytes": 65142784, + "loadedRssBytes": 68419584, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66314240, + "loadedPeakRssBytes": 69242880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "jan-pond-house-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71491584, + "startupRssBytes": 65925120, + "loadedRssBytes": 66842624, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66760704, + "loadedPeakRssBytes": 67538944, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70500352, + "startupRssBytes": 65695744, + "loadedRssBytes": 67137536, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66633728, + "loadedPeakRssBytes": 67809280, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78970880, + "startupRssBytes": 65470464, + "loadedRssBytes": 67436544, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66777088, + "loadedPeakRssBytes": 68116480, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 93093888, + "startupRssBytes": 65413120, + "loadedRssBytes": 68034560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66576384, + "loadedPeakRssBytes": 68816896, + "resourceUsageMaxRssBytes": 91906048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "graham-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72069120, + "startupRssBytes": 64765952, + "loadedRssBytes": 66338816, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65581056, + "loadedPeakRssBytes": 67194880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71299072, + "startupRssBytes": 65204224, + "loadedRssBytes": 67563520, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66428928, + "loadedPeakRssBytes": 68218880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78872576, + "startupRssBytes": 65208320, + "loadedRssBytes": 67305472, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66457600, + "loadedPeakRssBytes": 68055040, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 79687680, + "startupRssBytes": 65462272, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66789376, + "loadedPeakRssBytes": 69304320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gravo-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72118272, + "startupRssBytes": 64983040, + "loadedRssBytes": 66686976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66228224, + "loadedPeakRssBytes": 67579904, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70488064, + "startupRssBytes": 65179648, + "loadedRssBytes": 66883584, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66408448, + "loadedPeakRssBytes": 67633152, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79040512, + "startupRssBytes": 65675264, + "loadedRssBytes": 67117056, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66781184, + "loadedPeakRssBytes": 67846144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75771904, + "startupRssBytes": 65347584, + "loadedRssBytes": 68624384, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66322432, + "loadedPeakRssBytes": 69361664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "diego-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72146944, + "startupRssBytes": 65880064, + "loadedRssBytes": 67190784, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67063808, + "loadedPeakRssBytes": 68005888, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72073216, + "startupRssBytes": 65470464, + "loadedRssBytes": 67567616, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66408448, + "loadedPeakRssBytes": 68186112, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78852096, + "startupRssBytes": 65925120, + "loadedRssBytes": 67366912, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67059712, + "loadedPeakRssBytes": 68120576, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78807040, + "startupRssBytes": 65200128, + "loadedRssBytes": 68345856, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66367488, + "loadedPeakRssBytes": 69296128, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "grim-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72241152, + "startupRssBytes": 65159168, + "loadedRssBytes": 66994176, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66891776, + "loadedPeakRssBytes": 67862528, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71405568, + "startupRssBytes": 65433600, + "loadedRssBytes": 67399680, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66494464, + "loadedPeakRssBytes": 68214784, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78860288, + "startupRssBytes": 65060864, + "loadedRssBytes": 67158016, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66310144, + "loadedPeakRssBytes": 68034560, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75022336, + "startupRssBytes": 65683456, + "loadedRssBytes": 68567040, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 69443584, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "mercenary-near-grim-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71467008, + "startupRssBytes": 65024000, + "loadedRssBytes": 66072576, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66228224, + "loadedPeakRssBytes": 66879488, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71958528, + "startupRssBytes": 65830912, + "loadedRssBytes": 67403776, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66977792, + "loadedPeakRssBytes": 68313088, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78442496, + "startupRssBytes": 65724416, + "loadedRssBytes": 66904064, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66744320, + "loadedPeakRssBytes": 67756032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78852096, + "startupRssBytes": 64790528, + "loadedRssBytes": 68591616, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65409024, + "loadedPeakRssBytes": 69369856, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "popular-lock", + "solvers": { + "candidate": { + "peakRssBytes": 72925184, + "startupRssBytes": 64864256, + "loadedRssBytes": 67223552, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66072576, + "loadedPeakRssBytes": 67854336, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72818688, + "startupRssBytes": 65572864, + "loadedRssBytes": 67670016, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66842624, + "loadedPeakRssBytes": 68358144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78925824, + "startupRssBytes": 64843776, + "loadedRssBytes": 67465216, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66355200, + "loadedPeakRssBytes": 68124672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75907072, + "startupRssBytes": 65937408, + "loadedRssBytes": 67903488, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66772992, + "loadedPeakRssBytes": 68808704, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "alberto-mine-hut-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72032256, + "startupRssBytes": 65372160, + "loadedRssBytes": 66945024, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66519040, + "loadedPeakRssBytes": 67907584, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71299072, + "startupRssBytes": 66199552, + "loadedRssBytes": 67772416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67170304, + "loadedPeakRssBytes": 68464640, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78888960, + "startupRssBytes": 65069056, + "loadedRssBytes": 67166208, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66940928, + "loadedPeakRssBytes": 68124672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75685888, + "startupRssBytes": 65064960, + "loadedRssBytes": 68472832, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66490368, + "loadedPeakRssBytes": 69316608, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "alberto-mine-upstairs-left-chest", + "solvers": { + "candidate": { + "peakRssBytes": 70963200, + "startupRssBytes": 65175552, + "loadedRssBytes": 67010560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66551808, + "loadedPeakRssBytes": 67665920, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71032832, + "startupRssBytes": 65646592, + "loadedRssBytes": 67481600, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68358144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 71507968, + "startupRssBytes": 65769472, + "loadedRssBytes": 67080192, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66801664, + "loadedPeakRssBytes": 67776512, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 72077312, + "startupRssBytes": 65044480, + "loadedRssBytes": 68321280, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66244608, + "loadedPeakRssBytes": 69312512, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "alberto-mine-upstairs-right-chest", + "solvers": { + "candidate": { + "peakRssBytes": 70955008, + "startupRssBytes": 65347584, + "loadedRssBytes": 66658304, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 67461120, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70983680, + "startupRssBytes": 65761280, + "loadedRssBytes": 67334144, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66994176, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78843904, + "startupRssBytes": 65380352, + "loadedRssBytes": 67477504, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66445312, + "loadedPeakRssBytes": 68112384, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 73433088, + "startupRssBytes": 65761280, + "loadedRssBytes": 67727360, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66560000, + "loadedPeakRssBytes": 68489216, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "cavalorn-storeroom-door", + "solvers": { + "candidate": { + "peakRssBytes": 72126464, + "startupRssBytes": 65568768, + "loadedRssBytes": 67010560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66990080, + "loadedPeakRssBytes": 67907584, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71397376, + "startupRssBytes": 65282048, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66547712, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79187968, + "startupRssBytes": 65269760, + "loadedRssBytes": 67366912, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66609152, + "loadedPeakRssBytes": 68096000, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 85778432, + "startupRssBytes": 65150976, + "loadedRssBytes": 68558848, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66166784, + "loadedPeakRssBytes": 69103616, + "resourceUsageMaxRssBytes": 85106688, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "old-to-new-camp-bandits-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72785920, + "startupRssBytes": 65982464, + "loadedRssBytes": 67162112, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67121152, + "loadedPeakRssBytes": 67997696, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71737344, + "startupRssBytes": 65581056, + "loadedRssBytes": 67416064, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66609152, + "loadedPeakRssBytes": 68075520, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79044608, + "startupRssBytes": 65212416, + "loadedRssBytes": 67309568, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66351104, + "loadedPeakRssBytes": 68112384, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74752000, + "startupRssBytes": 65073152, + "loadedRssBytes": 68218880, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66187264, + "loadedPeakRssBytes": 69152768, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + } + ] + } +} diff --git a/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md b/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md new file mode 100644 index 0000000..2a11b80 --- /dev/null +++ b/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md @@ -0,0 +1,3858 @@ +# Gothic Lock Solver benchmark evidence + +Scope: **full-45-catalog**; quality: **passed**; performance: **inconclusive**. + +Warmups: 2; repetitions: 7. A = grouped actions, U = distinct plates, C = unit divisions, S = plate switches. + +Only synchronous public solver call; cloning, legacy/reference normalization, replay and metrics excluded. +Per-lock baseline-vs-itself calibration first; matched rounds rotate by lock and round. A non-pass receives one bounded repeat with all raw attempts retained. +Paired log-ratio mean with conservative 2.776 standard-error envelope; control sets tolerance. >=5 samples; >25% control envelope cannot certify a pass. + +| Implementation | A | U | C | S | Sum of per-lock medians (ms) | +| --- | ---: | ---: | ---: | ---: | ---: | +| originalBfs | 483 | 245 | 1727 | 438 | 2653.701 | +| upstream | 491 | 245 | 1695 | 446 | 1517.403 | +| candidate | 483 | 245 | 1723 | 438 | 47.363 | +| baseline | 483 | 245 | 1723 | 438 | 48.507 | + +| Lock | Optimum A | Implementation | A/U/C/S | Median / p95 ms | Quality vs base | Timing vs base | +| --- | ---: | --- | --- | --- | --- | --- | +| ruined-tower-door | 17 | originalBfs | 17/6/57/16 | 89.658 / 252.696 | | | +| ruined-tower-door | 17 | upstream | 17/6/57/16 | 93.831 / 132.731 | | | +| ruined-tower-door | 17 | candidate | 17/6/57/16 | 3.577 / 4.910 | passed | inconclusive | +| ruined-tower-door | 17 | baseline | 17/6/57/16 | 3.509 / 5.843 | | | +| scatty-chest | 9 | originalBfs | 9/5/31/8 | 12.406 / 33.726 | | | +| scatty-chest | 9 | upstream | 9/5/31/8 | 5.647 / 6.514 | | | +| scatty-chest | 9 | candidate | 9/5/31/8 | 0.137 / 0.165 | passed | inconclusive | +| scatty-chest | 9 | baseline | 9/5/31/8 | 0.138 / 0.198 | | | +| cor-galom-chest | 5 | originalBfs | 5/4/19/4 | 0.506 / 0.552 | | | +| cor-galom-chest | 5 | upstream | 5/4/15/4 | 0.343 / 0.463 | | | +| cor-galom-chest | 5 | candidate | 5/4/15/4 | 0.081 / 0.116 | passed | inconclusive | +| cor-galom-chest | 5 | baseline | 5/4/15/4 | 0.040 / 0.134 | | | +| cor-galom-bedroom-chest | 14 | originalBfs | 14/5/60/13 | 71.330 / 78.037 | | | +| cor-galom-bedroom-chest | 14 | upstream | 14/5/60/13 | 32.020 / 32.880 | | | +| cor-galom-bedroom-chest | 14 | candidate | 14/5/60/13 | 0.238 / 0.327 | passed | inconclusive | +| cor-galom-bedroom-chest | 14 | baseline | 14/5/60/13 | 0.199 / 0.222 | | | +| swamp-camp-forge-chest | 11 | originalBfs | 11/6/41/10 | 106.387 / 120.809 | | | +| swamp-camp-forge-chest | 11 | upstream | 11/6/41/10 | 38.003 / 42.506 | | | +| swamp-camp-forge-chest | 11 | candidate | 11/6/41/10 | 0.241 / 0.461 | passed | inconclusive | +| swamp-camp-forge-chest | 11 | baseline | 11/6/41/10 | 0.212 / 0.247 | | | +| rice-lord-storeroom | 9 | originalBfs | 9/5/30/8 | 8.856 / 9.087 | | | +| rice-lord-storeroom | 9 | upstream | 9/5/30/8 | 4.829 / 5.141 | | | +| rice-lord-storeroom | 9 | candidate | 9/5/30/8 | 0.159 / 0.227 | passed | inconclusive | +| rice-lord-storeroom | 9 | baseline | 9/5/30/8 | 0.124 / 0.127 | | | +| gomez-chambers-door | 9 | originalBfs | 9/6/28/8 | 74.677 / 77.957 | | | +| gomez-chambers-door | 9 | upstream | 9/6/28/8 | 31.106 / 32.987 | | | +| gomez-chambers-door | 9 | candidate | 9/6/28/8 | 0.202 / 0.245 | passed | inconclusive | +| gomez-chambers-door | 9 | baseline | 9/6/28/8 | 0.171 / 0.238 | | | +| gomez-throne-hall-chest | 5 | originalBfs | 5/4/16/4 | 0.179 / 0.220 | | | +| gomez-throne-hall-chest | 5 | upstream | 5/4/16/4 | 0.239 / 0.333 | | | +| gomez-throne-hall-chest | 5 | candidate | 5/4/16/4 | 0.025 / 0.052 | passed | inconclusive | +| gomez-throne-hall-chest | 5 | baseline | 5/4/16/4 | 0.025 / 0.031 | | | +| old-camp-dungeon-door | 17 | originalBfs | 17/6/72/16 | 90.523 / 125.406 | | | +| old-camp-dungeon-door | 17 | upstream | 18/6/68/17 | 42.867 / 49.406 | | | +| old-camp-dungeon-door | 17 | candidate | 17/6/72/16 | 2.244 / 2.823 | passed | inconclusive | +| old-camp-dungeon-door | 17 | baseline | 17/6/72/16 | 2.279 / 2.316 | | | +| arlin-dungeon-key | 7 | originalBfs | 7/5/25/6 | 48.473 / 50.914 | | | +| arlin-dungeon-key | 7 | upstream | 7/5/25/6 | 21.404 / 21.503 | | | +| arlin-dungeon-key | 7 | candidate | 7/5/25/6 | 7.950 / 11.083 | passed | inconclusive | +| arlin-dungeon-key | 7 | baseline | 7/5/25/6 | 8.874 / 11.835 | | | +| gomez-wine-cellar | 9 | originalBfs | 9/6/41/8 | 59.599 / 73.166 | | | +| gomez-wine-cellar | 9 | upstream | 11/6/31/10 | 28.340 / 30.370 | | | +| gomez-wine-cellar | 9 | candidate | 9/6/41/8 | 0.119 / 0.228 | passed | inconclusive | +| gomez-wine-cellar | 9 | baseline | 9/6/41/8 | 0.092 / 0.168 | | | +| gomez-bedside-chest | 23 | originalBfs | 23/5/68/22 | 10.780 / 11.636 | | | +| gomez-bedside-chest | 23 | upstream | 23/5/68/22 | 8.770 / 10.758 | | | +| gomez-bedside-chest | 23 | candidate | 23/5/68/22 | 1.644 / 2.551 | passed | inconclusive | +| gomez-bedside-chest | 23 | baseline | 23/5/68/22 | 1.568 / 2.397 | | | +| gomez-wall-chest | 20 | originalBfs | 20/6/75/19 | 101.362 / 108.772 | | | +| gomez-wall-chest | 20 | upstream | 20/6/75/19 | 87.626 / 104.459 | | | +| gomez-wall-chest | 20 | candidate | 20/6/75/19 | 5.744 / 7.457 | passed | inconclusive | +| gomez-wall-chest | 20 | baseline | 20/6/75/19 | 5.674 / 10.003 | | | +| voran-bedside-chest | 9 | originalBfs | 9/7/38/8 | 199.166 / 217.744 | | | +| voran-bedside-chest | 9 | upstream | 9/7/38/8 | 188.301 / 219.238 | | | +| voran-bedside-chest | 9 | candidate | 9/7/38/8 | 0.112 / 0.184 | passed | inconclusive | +| voran-bedside-chest | 9 | baseline | 9/7/38/8 | 0.129 / 0.531 | | | +| arto-room-door | 7 | originalBfs | 7/5/31/6 | 2.398 / 3.201 | | | +| arto-room-door | 7 | upstream | 7/5/31/6 | 4.815 / 5.955 | | | +| arto-room-door | 7 | candidate | 7/5/31/6 | 0.050 / 0.060 | passed | inconclusive | +| arto-room-door | 7 | baseline | 7/5/31/6 | 0.046 / 0.084 | | | +| arto-far-bed-chest | 11 | originalBfs | 11/5/44/10 | 86.226 / 89.989 | | | +| arto-far-bed-chest | 11 | upstream | 13/5/40/12 | 27.589 / 32.241 | | | +| arto-far-bed-chest | 11 | candidate | 11/5/44/10 | 0.168 / 0.496 | passed | inconclusive | +| arto-far-bed-chest | 11 | baseline | 11/5/44/10 | 0.156 / 0.194 | | | +| arto-near-bed-chest | 13 | originalBfs | 13/6/52/12 | 105.042 / 116.204 | | | +| arto-near-bed-chest | 13 | upstream | 13/6/52/12 | 76.194 / 102.692 | | | +| arto-near-bed-chest | 13 | candidate | 13/6/52/12 | 0.449 / 0.487 | passed | inconclusive | +| arto-near-bed-chest | 13 | baseline | 13/6/52/12 | 0.477 / 0.751 | | | +| gomez-bedrooms-door | 14 | originalBfs | 14/6/45/13 | 104.103 / 110.613 | | | +| gomez-bedrooms-door | 14 | upstream | 14/6/45/13 | 38.404 / 41.819 | | | +| gomez-bedrooms-door | 14 | candidate | 14/6/49/13 | 5.739 / 7.218 | passed | inconclusive | +| gomez-bedrooms-door | 14 | baseline | 14/6/49/13 | 5.763 / 8.625 | | | +| gomez-bedrooms-far-chest | 14 | originalBfs | 14/5/43/13 | 11.208 / 15.988 | | | +| gomez-bedrooms-far-chest | 14 | upstream | 16/5/39/15 | 5.621 / 6.539 | | | +| gomez-bedrooms-far-chest | 14 | candidate | 14/5/43/13 | 1.372 / 2.125 | passed | passed | +| gomez-bedrooms-far-chest | 14 | baseline | 14/5/43/13 | 1.553 / 2.375 | | | +| gomez-bedrooms-near-chest | 12 | originalBfs | 12/6/56/11 | 78.994 / 89.428 | | | +| gomez-bedrooms-near-chest | 12 | upstream | 12/6/56/11 | 43.647 / 49.915 | | | +| gomez-bedrooms-near-chest | 12 | candidate | 12/6/56/11 | 0.099 / 0.104 | passed | inconclusive | +| gomez-bedrooms-near-chest | 12 | baseline | 12/6/56/11 | 0.111 / 0.436 | | | +| raven-tower-left-chest | 11 | originalBfs | 11/6/35/10 | 109.446 / 128.538 | | | +| raven-tower-left-chest | 11 | upstream | 11/6/35/10 | 52.739 / 53.759 | | | +| raven-tower-left-chest | 11 | candidate | 11/6/35/10 | 1.455 / 1.825 | passed | inconclusive | +| raven-tower-left-chest | 11 | baseline | 11/6/35/10 | 1.340 / 1.867 | | | +| raven-tower-right-chest | 8 | originalBfs | 8/6/23/7 | 36.350 / 38.205 | | | +| raven-tower-right-chest | 8 | upstream | 8/6/23/7 | 16.226 / 16.684 | | | +| raven-tower-right-chest | 8 | candidate | 8/6/23/7 | 0.134 / 0.156 | passed | inconclusive | +| raven-tower-right-chest | 8 | baseline | 8/6/23/7 | 0.125 / 0.245 | | | +| raven-tower-floor2-stairs-chest | 16 | originalBfs | 16/6/48/15 | 103.036 / 108.319 | | | +| raven-tower-floor2-stairs-chest | 16 | upstream | 16/6/48/15 | 62.861 / 69.600 | | | +| raven-tower-floor2-stairs-chest | 16 | candidate | 16/6/48/15 | 4.291 / 5.547 | passed | passed | +| raven-tower-floor2-stairs-chest | 16 | baseline | 16/6/48/15 | 4.655 / 5.491 | | | +| raven-tower-floor2-chest-2 | 8 | originalBfs | 8/5/29/7 | 10.942 / 12.065 | | | +| raven-tower-floor2-chest-2 | 8 | upstream | 9/5/27/8 | 3.235 / 3.589 | | | +| raven-tower-floor2-chest-2 | 8 | candidate | 8/5/29/7 | 0.190 / 0.268 | passed | inconclusive | +| raven-tower-floor2-chest-2 | 8 | baseline | 8/5/29/7 | 0.188 / 0.385 | | | +| raven-tower-floor3-entrance-chest | 14 | originalBfs | 14/5/44/13 | 8.888 / 9.141 | | | +| raven-tower-floor3-entrance-chest | 14 | upstream | 14/5/44/13 | 6.582 / 6.925 | | | +| raven-tower-floor3-entrance-chest | 14 | candidate | 14/5/44/13 | 0.606 / 1.505 | passed | inconclusive | +| raven-tower-floor3-entrance-chest | 14 | baseline | 14/5/44/13 | 0.616 / 0.838 | | | +| raven-tower-floor3-empty-chest | 9 | originalBfs | 9/5/41/8 | 7.984 / 8.417 | | | +| raven-tower-floor3-empty-chest | 9 | upstream | 9/5/37/8 | 5.718 / 6.900 | | | +| raven-tower-floor3-empty-chest | 9 | candidate | 9/5/37/8 | 0.077 / 0.103 | passed | inconclusive | +| raven-tower-floor3-empty-chest | 9 | baseline | 9/5/37/8 | 0.076 / 0.151 | | | +| raven-tower-floor4-elixirs-chest | 8 | originalBfs | 8/5/36/7 | 75.479 / 83.178 | | | +| raven-tower-floor4-elixirs-chest | 8 | upstream | 8/5/36/7 | 111.610 / 121.124 | | | +| raven-tower-floor4-elixirs-chest | 8 | candidate | 8/5/36/7 | 0.102 / 0.179 | passed | passed | +| raven-tower-floor4-elixirs-chest | 8 | baseline | 8/5/36/7 | 0.179 / 0.379 | | | +| raven-tower-floor4-food-chest | 11 | originalBfs | 11/6/43/10 | 95.996 / 102.990 | | | +| raven-tower-floor4-food-chest | 11 | upstream | 11/6/43/10 | 57.610 / 61.539 | | | +| raven-tower-floor4-food-chest | 11 | candidate | 11/6/43/10 | 0.253 / 0.272 | passed | inconclusive | +| raven-tower-floor4-food-chest | 11 | baseline | 11/6/43/10 | 0.281 / 0.690 | | | +| raven-tower-top-chest | 20 | originalBfs | 20/6/48/19 | 83.326 / 92.664 | | | +| raven-tower-top-chest | 20 | upstream | 20/6/48/19 | 46.343 / 50.414 | | | +| raven-tower-top-chest | 20 | candidate | 20/6/48/19 | 7.368 / 9.387 | passed | inconclusive | +| raven-tower-top-chest | 20 | baseline | 20/6/48/19 | 7.451 / 7.709 | | | +| fire-lizard-cave-chest | 10 | originalBfs | 10/6/41/9 | 67.629 / 77.178 | | | +| fire-lizard-cave-chest | 10 | upstream | 10/6/41/9 | 56.070 / 58.654 | | | +| fire-lizard-cave-chest | 10 | candidate | 10/6/41/9 | 0.160 / 0.230 | passed | inconclusive | +| fire-lizard-cave-chest | 10 | baseline | 10/6/41/9 | 0.152 / 0.850 | | | +| silas-tavern-basement-chest | 9 | originalBfs | 9/5/30/8 | 6.911 / 7.779 | | | +| silas-tavern-basement-chest | 9 | upstream | 9/5/30/8 | 4.053 / 4.463 | | | +| silas-tavern-basement-chest | 9 | candidate | 9/5/30/8 | 0.100 / 0.233 | passed | inconclusive | +| silas-tavern-basement-chest | 9 | baseline | 9/5/30/8 | 0.089 / 0.106 | | | +| silas-tavern-storeroom-chest | 9 | originalBfs | 9/6/31/8 | 87.225 / 93.561 | | | +| silas-tavern-storeroom-chest | 9 | upstream | 9/6/31/8 | 30.919 / 31.912 | | | +| silas-tavern-storeroom-chest | 9 | candidate | 9/6/31/8 | 0.087 / 0.157 | passed | inconclusive | +| silas-tavern-storeroom-chest | 9 | baseline | 9/6/31/8 | 0.080 / 0.103 | | | +| silas-tavern-storeroom-door | 10 | originalBfs | 10/5/31/9 | 78.692 / 85.264 | | | +| silas-tavern-storeroom-door | 10 | upstream | 10/5/31/9 | 23.705 / 27.696 | | | +| silas-tavern-storeroom-door | 10 | candidate | 10/5/31/9 | 0.158 / 0.179 | passed | passed | +| silas-tavern-storeroom-door | 10 | baseline | 10/5/31/9 | 0.169 / 0.183 | | | +| jan-pond-house-chest | 8 | originalBfs | 8/6/33/7 | 45.814 / 58.215 | | | +| jan-pond-house-chest | 8 | upstream | 8/6/33/7 | 51.663 / 56.488 | | | +| jan-pond-house-chest | 8 | candidate | 8/6/33/7 | 0.085 / 0.092 | passed | inconclusive | +| jan-pond-house-chest | 8 | baseline | 8/6/33/7 | 0.075 / 0.097 | | | +| graham-chest | 10 | originalBfs | 10/6/40/9 | 97.997 / 111.180 | | | +| graham-chest | 10 | upstream | 10/6/40/9 | 31.137 / 32.488 | | | +| graham-chest | 10 | candidate | 10/6/40/9 | 0.091 / 0.093 | passed | inconclusive | +| graham-chest | 10 | baseline | 10/6/40/9 | 0.083 / 0.099 | | | +| gravo-chest | 8 | originalBfs | 8/5/27/7 | 5.361 / 6.206 | | | +| gravo-chest | 8 | upstream | 8/5/27/7 | 5.337 / 5.990 | | | +| gravo-chest | 8 | candidate | 8/5/27/7 | 0.063 / 0.081 | passed | inconclusive | +| gravo-chest | 8 | baseline | 8/5/27/7 | 0.055 / 0.071 | | | +| diego-chest | 11 | originalBfs | 11/6/40/10 | 81.415 / 84.984 | | | +| diego-chest | 11 | upstream | 11/6/40/10 | 31.231 / 47.637 | | | +| diego-chest | 11 | candidate | 11/6/40/10 | 0.147 / 0.180 | passed | passed | +| diego-chest | 11 | baseline | 11/6/40/10 | 0.149 / 0.177 | | | +| grim-chest | 8 | originalBfs | 8/6/21/7 | 107.132 / 123.995 | | | +| grim-chest | 8 | upstream | 8/6/21/7 | 22.792 / 23.272 | | | +| grim-chest | 8 | candidate | 8/6/21/7 | 0.221 / 0.274 | passed | inconclusive | +| grim-chest | 8 | baseline | 8/6/21/7 | 0.196 / 0.289 | | | +| mercenary-near-grim-chest | 8 | originalBfs | 8/6/25/7 | 103.700 / 115.461 | | | +| mercenary-near-grim-chest | 8 | upstream | 8/6/25/7 | 32.990 / 35.385 | | | +| mercenary-near-grim-chest | 8 | candidate | 8/6/25/7 | 0.091 / 0.126 | passed | inconclusive | +| mercenary-near-grim-chest | 8 | baseline | 8/6/25/7 | 0.075 / 0.096 | | | +| popular-lock | 13 | originalBfs | 13/5/46/12 | 10.440 / 12.194 | | | +| popular-lock | 13 | upstream | 13/5/46/12 | 6.057 / 7.192 | | | +| popular-lock | 13 | candidate | 13/5/46/12 | 0.573 / 0.721 | passed | passed | +| popular-lock | 13 | baseline | 13/5/46/12 | 0.600 / 0.630 | | | +| alberto-mine-hut-chest | 9 | originalBfs | 9/5/36/8 | 9.577 / 15.550 | | | +| alberto-mine-hut-chest | 9 | upstream | 9/5/36/8 | 5.696 / 5.980 | | | +| alberto-mine-hut-chest | 9 | candidate | 9/5/36/8 | 0.063 / 0.073 | passed | inconclusive | +| alberto-mine-hut-chest | 9 | baseline | 9/5/36/8 | 0.057 / 0.160 | | | +| alberto-mine-upstairs-left-chest | 4 | originalBfs | 4/3/20/3 | 0.040 / 0.041 | | | +| alberto-mine-upstairs-left-chest | 4 | upstream | 4/3/20/3 | 0.217 / 0.431 | | | +| alberto-mine-upstairs-left-chest | 4 | candidate | 4/3/20/3 | 0.021 / 0.102 | passed | inconclusive | +| alberto-mine-upstairs-left-chest | 4 | baseline | 4/3/20/3 | 0.018 / 0.022 | | | +| alberto-mine-upstairs-right-chest | 5 | originalBfs | 5/5/13/4 | 2.712 / 3.271 | | | +| alberto-mine-upstairs-right-chest | 5 | upstream | 5/5/13/4 | 1.724 / 1.913 | | | +| alberto-mine-upstairs-right-chest | 5 | candidate | 5/5/13/4 | 0.035 / 0.279 | passed | inconclusive | +| alberto-mine-upstairs-right-chest | 5 | baseline | 5/5/13/4 | 0.026 / 0.045 | | | +| cavalorn-storeroom-door | 11 | originalBfs | 11/6/41/10 | 85.848 / 89.011 | | | +| cavalorn-storeroom-door | 11 | upstream | 11/6/41/10 | 43.419 / 47.827 | | | +| cavalorn-storeroom-door | 11 | candidate | 11/6/41/10 | 0.220 / 0.254 | passed | inconclusive | +| cavalorn-storeroom-door | 11 | baseline | 11/6/41/10 | 0.211 / 0.274 | | | +| old-to-new-camp-bandits-chest | 10 | originalBfs | 10/5/33/9 | 69.890 / 75.509 | | | +| old-to-new-camp-bandits-chest | 10 | upstream | 10/5/33/9 | 27.873 / 30.980 | | | +| old-to-new-camp-bandits-chest | 10 | candidate | 10/5/33/9 | 0.422 / 0.567 | passed | inconclusive | +| old-to-new-camp-bandits-chest | 10 | baseline | 10/5/33/9 | 0.421 / 0.540 | | | + +## Per-lock candidate differences + +| Lock | Comparator | ΔA / ΔU / ΔC / ΔS | +| --- | --- | --- | +| ruined-tower-door | originalBfs | 0/0/0/0 | +| ruined-tower-door | upstream | 0/0/0/0 | +| ruined-tower-door | baseline | 0/0/0/0 | +| scatty-chest | originalBfs | 0/0/0/0 | +| scatty-chest | upstream | 0/0/0/0 | +| scatty-chest | baseline | 0/0/0/0 | +| cor-galom-chest | originalBfs | 0/0/-4/0 | +| cor-galom-chest | upstream | 0/0/0/0 | +| cor-galom-chest | baseline | 0/0/0/0 | +| cor-galom-bedroom-chest | originalBfs | 0/0/0/0 | +| cor-galom-bedroom-chest | upstream | 0/0/0/0 | +| cor-galom-bedroom-chest | baseline | 0/0/0/0 | +| swamp-camp-forge-chest | originalBfs | 0/0/0/0 | +| swamp-camp-forge-chest | upstream | 0/0/0/0 | +| swamp-camp-forge-chest | baseline | 0/0/0/0 | +| rice-lord-storeroom | originalBfs | 0/0/0/0 | +| rice-lord-storeroom | upstream | 0/0/0/0 | +| rice-lord-storeroom | baseline | 0/0/0/0 | +| gomez-chambers-door | originalBfs | 0/0/0/0 | +| gomez-chambers-door | upstream | 0/0/0/0 | +| gomez-chambers-door | baseline | 0/0/0/0 | +| gomez-throne-hall-chest | originalBfs | 0/0/0/0 | +| gomez-throne-hall-chest | upstream | 0/0/0/0 | +| gomez-throne-hall-chest | baseline | 0/0/0/0 | +| old-camp-dungeon-door | originalBfs | 0/0/0/0 | +| old-camp-dungeon-door | upstream | -1/0/4/-1 | +| old-camp-dungeon-door | baseline | 0/0/0/0 | +| arlin-dungeon-key | originalBfs | 0/0/0/0 | +| arlin-dungeon-key | upstream | 0/0/0/0 | +| arlin-dungeon-key | baseline | 0/0/0/0 | +| gomez-wine-cellar | originalBfs | 0/0/0/0 | +| gomez-wine-cellar | upstream | -2/0/10/-2 | +| gomez-wine-cellar | baseline | 0/0/0/0 | +| gomez-bedside-chest | originalBfs | 0/0/0/0 | +| gomez-bedside-chest | upstream | 0/0/0/0 | +| gomez-bedside-chest | baseline | 0/0/0/0 | +| gomez-wall-chest | originalBfs | 0/0/0/0 | +| gomez-wall-chest | upstream | 0/0/0/0 | +| gomez-wall-chest | baseline | 0/0/0/0 | +| voran-bedside-chest | originalBfs | 0/0/0/0 | +| voran-bedside-chest | upstream | 0/0/0/0 | +| voran-bedside-chest | baseline | 0/0/0/0 | +| arto-room-door | originalBfs | 0/0/0/0 | +| arto-room-door | upstream | 0/0/0/0 | +| arto-room-door | baseline | 0/0/0/0 | +| arto-far-bed-chest | originalBfs | 0/0/0/0 | +| arto-far-bed-chest | upstream | -2/0/4/-2 | +| arto-far-bed-chest | baseline | 0/0/0/0 | +| arto-near-bed-chest | originalBfs | 0/0/0/0 | +| arto-near-bed-chest | upstream | 0/0/0/0 | +| arto-near-bed-chest | baseline | 0/0/0/0 | +| gomez-bedrooms-door | originalBfs | 0/0/4/0 | +| gomez-bedrooms-door | upstream | 0/0/4/0 | +| gomez-bedrooms-door | baseline | 0/0/0/0 | +| gomez-bedrooms-far-chest | originalBfs | 0/0/0/0 | +| gomez-bedrooms-far-chest | upstream | -2/0/4/-2 | +| gomez-bedrooms-far-chest | baseline | 0/0/0/0 | +| gomez-bedrooms-near-chest | originalBfs | 0/0/0/0 | +| gomez-bedrooms-near-chest | upstream | 0/0/0/0 | +| gomez-bedrooms-near-chest | baseline | 0/0/0/0 | +| raven-tower-left-chest | originalBfs | 0/0/0/0 | +| raven-tower-left-chest | upstream | 0/0/0/0 | +| raven-tower-left-chest | baseline | 0/0/0/0 | +| raven-tower-right-chest | originalBfs | 0/0/0/0 | +| raven-tower-right-chest | upstream | 0/0/0/0 | +| raven-tower-right-chest | baseline | 0/0/0/0 | +| raven-tower-floor2-stairs-chest | originalBfs | 0/0/0/0 | +| raven-tower-floor2-stairs-chest | upstream | 0/0/0/0 | +| raven-tower-floor2-stairs-chest | baseline | 0/0/0/0 | +| raven-tower-floor2-chest-2 | originalBfs | 0/0/0/0 | +| raven-tower-floor2-chest-2 | upstream | -1/0/2/-1 | +| raven-tower-floor2-chest-2 | baseline | 0/0/0/0 | +| raven-tower-floor3-entrance-chest | originalBfs | 0/0/0/0 | +| raven-tower-floor3-entrance-chest | upstream | 0/0/0/0 | +| raven-tower-floor3-entrance-chest | baseline | 0/0/0/0 | +| raven-tower-floor3-empty-chest | originalBfs | 0/0/-4/0 | +| raven-tower-floor3-empty-chest | upstream | 0/0/0/0 | +| raven-tower-floor3-empty-chest | baseline | 0/0/0/0 | +| raven-tower-floor4-elixirs-chest | originalBfs | 0/0/0/0 | +| raven-tower-floor4-elixirs-chest | upstream | 0/0/0/0 | +| raven-tower-floor4-elixirs-chest | baseline | 0/0/0/0 | +| raven-tower-floor4-food-chest | originalBfs | 0/0/0/0 | +| raven-tower-floor4-food-chest | upstream | 0/0/0/0 | +| raven-tower-floor4-food-chest | baseline | 0/0/0/0 | +| raven-tower-top-chest | originalBfs | 0/0/0/0 | +| raven-tower-top-chest | upstream | 0/0/0/0 | +| raven-tower-top-chest | baseline | 0/0/0/0 | +| fire-lizard-cave-chest | originalBfs | 0/0/0/0 | +| fire-lizard-cave-chest | upstream | 0/0/0/0 | +| fire-lizard-cave-chest | baseline | 0/0/0/0 | +| silas-tavern-basement-chest | originalBfs | 0/0/0/0 | +| silas-tavern-basement-chest | upstream | 0/0/0/0 | +| silas-tavern-basement-chest | baseline | 0/0/0/0 | +| silas-tavern-storeroom-chest | originalBfs | 0/0/0/0 | +| silas-tavern-storeroom-chest | upstream | 0/0/0/0 | +| silas-tavern-storeroom-chest | baseline | 0/0/0/0 | +| silas-tavern-storeroom-door | originalBfs | 0/0/0/0 | +| silas-tavern-storeroom-door | upstream | 0/0/0/0 | +| silas-tavern-storeroom-door | baseline | 0/0/0/0 | +| jan-pond-house-chest | originalBfs | 0/0/0/0 | +| jan-pond-house-chest | upstream | 0/0/0/0 | +| jan-pond-house-chest | baseline | 0/0/0/0 | +| graham-chest | originalBfs | 0/0/0/0 | +| graham-chest | upstream | 0/0/0/0 | +| graham-chest | baseline | 0/0/0/0 | +| gravo-chest | originalBfs | 0/0/0/0 | +| gravo-chest | upstream | 0/0/0/0 | +| gravo-chest | baseline | 0/0/0/0 | +| diego-chest | originalBfs | 0/0/0/0 | +| diego-chest | upstream | 0/0/0/0 | +| diego-chest | baseline | 0/0/0/0 | +| grim-chest | originalBfs | 0/0/0/0 | +| grim-chest | upstream | 0/0/0/0 | +| grim-chest | baseline | 0/0/0/0 | +| mercenary-near-grim-chest | originalBfs | 0/0/0/0 | +| mercenary-near-grim-chest | upstream | 0/0/0/0 | +| mercenary-near-grim-chest | baseline | 0/0/0/0 | +| popular-lock | originalBfs | 0/0/0/0 | +| popular-lock | upstream | 0/0/0/0 | +| popular-lock | baseline | 0/0/0/0 | +| alberto-mine-hut-chest | originalBfs | 0/0/0/0 | +| alberto-mine-hut-chest | upstream | 0/0/0/0 | +| alberto-mine-hut-chest | baseline | 0/0/0/0 | +| alberto-mine-upstairs-left-chest | originalBfs | 0/0/0/0 | +| alberto-mine-upstairs-left-chest | upstream | 0/0/0/0 | +| alberto-mine-upstairs-left-chest | baseline | 0/0/0/0 | +| alberto-mine-upstairs-right-chest | originalBfs | 0/0/0/0 | +| alberto-mine-upstairs-right-chest | upstream | 0/0/0/0 | +| alberto-mine-upstairs-right-chest | baseline | 0/0/0/0 | +| cavalorn-storeroom-door | originalBfs | 0/0/0/0 | +| cavalorn-storeroom-door | upstream | 0/0/0/0 | +| cavalorn-storeroom-door | baseline | 0/0/0/0 | +| old-to-new-camp-bandits-chest | originalBfs | 0/0/0/0 | +| old-to-new-camp-bandits-chest | upstream | 0/0/0/0 | +| old-to-new-camp-bandits-chest | baseline | 0/0/0/0 | + +## Isolated process peak RSS + +RSS includes Node, module loading and runtime allocation; it is not exact algorithm heap allocation. Browser peak memory is unavailable. Memory runs are separate from timed calls. + +```json +{ + "metric": "Linux /proc/self/status VmHWM × 1024 bytes (current process-image RSS high-water)", + "repetitions": 5, + "sources": [ + "https://docs.kernel.org/filesystems/proc.html", + "https://nodejs.org/api/process.html#processresourceusage" + ], + "accuracy": "Kernel RSS accounting is asynchronous and approximate; this is not an exact allocation count. Raw getrusage maxRSS and startup/current-image peaks are preserved to expose pre-exec launch floors.", + "scope": "Each per-lock observation is a fresh process. Repeated comparison is peak RSS of the selected 45-lock workload per fresh process.", + "includes": "Node runtime, TypeScript benchmark loader, module loading, fixture loading, normalization, independent replay and solver allocations. Startup and loaded current RSS are recorded separately; subtraction is not exact algorithm allocation.", + "browser": { + "available": false, + "reason": "No portable exact browser peak-memory API; Node RSS is not browser memory." + }, + "performance": { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.962246756993597, + "noiseRatio": 1.0949647700001646, + "lowerRatio": 0.8908822461111241, + "upperRatio": 1.0393279531458977 + }, + "attempts": [ + { + "verdict": "passed", + "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", + "ratio": 0.962246756993597, + "noiseRatio": 1.0949647700001646, + "lowerRatio": 0.8908822461111241, + "upperRatio": 1.0393279531458977, + "samples": { + "candidate": [ + { + "peakRssBytes": 94666752, + "startupRssBytes": 64344064, + "loadedRssBytes": 65785856, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65351680, + "loadedPeakRssBytes": 66846720, + "resourceUsageMaxRssBytes": 93732864, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94412800, + "startupRssBytes": 65175552, + "loadedRssBytes": 66617344, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66248704, + "loadedPeakRssBytes": 67477504, + "resourceUsageMaxRssBytes": 93741056, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94752768, + "startupRssBytes": 65814528, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66715648, + "loadedPeakRssBytes": 67567616, + "resourceUsageMaxRssBytes": 94523392, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89186304, + "startupRssBytes": 65310720, + "loadedRssBytes": 66883584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66576384, + "loadedPeakRssBytes": 67739648, + "resourceUsageMaxRssBytes": 89006080, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84447232, + "startupRssBytes": 65482752, + "loadedRssBytes": 67186688, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66744320, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "baseline": [ + { + "peakRssBytes": 95948800, + "startupRssBytes": 64970752, + "loadedRssBytes": 67461120, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66179072, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 95100928, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89391104, + "startupRssBytes": 64778240, + "loadedRssBytes": 67137536, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65925120, + "loadedPeakRssBytes": 68210688, + "resourceUsageMaxRssBytes": 88641536, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 100007936, + "startupRssBytes": 64532480, + "loadedRssBytes": 67678208, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65564672, + "loadedPeakRssBytes": 68284416, + "resourceUsageMaxRssBytes": 100007936, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95895552, + "startupRssBytes": 65687552, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67055616, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 95895552, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 93995008, + "startupRssBytes": 65671168, + "loadedRssBytes": 67375104, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66916352, + "loadedPeakRssBytes": 68136960, + "resourceUsageMaxRssBytes": 93188096, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "originalBfs": [ + { + "peakRssBytes": 121143296, + "startupRssBytes": 65421312, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66490368, + "loadedPeakRssBytes": 67485696, + "resourceUsageMaxRssBytes": 120414208, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120922112, + "startupRssBytes": 65372160, + "loadedRssBytes": 67207168, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 120160256, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120438784, + "startupRssBytes": 64557056, + "loadedRssBytes": 66392064, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65339392, + "loadedPeakRssBytes": 67448832, + "resourceUsageMaxRssBytes": 119697408, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120311808, + "startupRssBytes": 65306624, + "loadedRssBytes": 67141632, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66830336, + "loadedPeakRssBytes": 68071424, + "resourceUsageMaxRssBytes": 119595008, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 121356288, + "startupRssBytes": 65691648, + "loadedRssBytes": 67395584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 68116480, + "resourceUsageMaxRssBytes": 120606720, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "upstream": [ + { + "peakRssBytes": 270356480, + "startupRssBytes": 65941504, + "loadedRssBytes": 68431872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67108864, + "loadedPeakRssBytes": 69251072, + "resourceUsageMaxRssBytes": 269815808, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270110720, + "startupRssBytes": 65540096, + "loadedRssBytes": 68685824, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 69443584, + "resourceUsageMaxRssBytes": 269246464, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 268992512, + "startupRssBytes": 64909312, + "loadedRssBytes": 68710400, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65966080, + "loadedPeakRssBytes": 69349376, + "resourceUsageMaxRssBytes": 267882496, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 309043200, + "startupRssBytes": 65306624, + "loadedRssBytes": 68452352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66768896, + "loadedPeakRssBytes": 69234688, + "resourceUsageMaxRssBytes": 309043200, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270135296, + "startupRssBytes": 65089536, + "loadedRssBytes": 68497408, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65785856, + "loadedPeakRssBytes": 69300224, + "resourceUsageMaxRssBytes": 269443072, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "baselineSelfA": [ + { + "peakRssBytes": 95563776, + "startupRssBytes": 65470464, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66633728, + "loadedPeakRssBytes": 68255744, + "resourceUsageMaxRssBytes": 95563776, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95862784, + "startupRssBytes": 65462272, + "loadedRssBytes": 67297280, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66928640, + "loadedPeakRssBytes": 68186112, + "resourceUsageMaxRssBytes": 95862784, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94965760, + "startupRssBytes": 65818624, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67014656, + "loadedPeakRssBytes": 68362240, + "resourceUsageMaxRssBytes": 94965760, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95129600, + "startupRssBytes": 65208320, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66326528, + "loadedPeakRssBytes": 68149248, + "resourceUsageMaxRssBytes": 94162944, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94371840, + "startupRssBytes": 64974848, + "loadedRssBytes": 66940928, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66097152, + "loadedPeakRssBytes": 67719168, + "resourceUsageMaxRssBytes": 94371840, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "baselineSelfB": [ + { + "peakRssBytes": 96808960, + "startupRssBytes": 64933888, + "loadedRssBytes": 67424256, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66068480, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 96731136, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94896128, + "startupRssBytes": 65703936, + "loadedRssBytes": 67407872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 94896128, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84250624, + "startupRssBytes": 65826816, + "loadedRssBytes": 67530752, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66949120, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 97107968, + "startupRssBytes": 65032192, + "loadedRssBytes": 66998272, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66183168, + "loadedPeakRssBytes": 67727360, + "resourceUsageMaxRssBytes": 97107968, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94167040, + "startupRssBytes": 64937984, + "loadedRssBytes": 67428352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66555904, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 93442048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ] + } + } + ], + "wholeCatalog": { + "candidate": { + "samples": [ + { + "peakRssBytes": 94666752, + "startupRssBytes": 64344064, + "loadedRssBytes": 65785856, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65351680, + "loadedPeakRssBytes": 66846720, + "resourceUsageMaxRssBytes": 93732864, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94412800, + "startupRssBytes": 65175552, + "loadedRssBytes": 66617344, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66248704, + "loadedPeakRssBytes": 67477504, + "resourceUsageMaxRssBytes": 93741056, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94752768, + "startupRssBytes": 65814528, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66715648, + "loadedPeakRssBytes": 67567616, + "resourceUsageMaxRssBytes": 94523392, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89186304, + "startupRssBytes": 65310720, + "loadedRssBytes": 66883584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66576384, + "loadedPeakRssBytes": 67739648, + "resourceUsageMaxRssBytes": 89006080, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84447232, + "startupRssBytes": 65482752, + "loadedRssBytes": 67186688, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66744320, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 84447232, + "median": 94412800, + "p95": 94752768, + "max": 94752768 + } + }, + "baseline": { + "samples": [ + { + "peakRssBytes": 95948800, + "startupRssBytes": 64970752, + "loadedRssBytes": 67461120, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66179072, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 95100928, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 89391104, + "startupRssBytes": 64778240, + "loadedRssBytes": 67137536, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65925120, + "loadedPeakRssBytes": 68210688, + "resourceUsageMaxRssBytes": 88641536, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 100007936, + "startupRssBytes": 64532480, + "loadedRssBytes": 67678208, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65564672, + "loadedPeakRssBytes": 68284416, + "resourceUsageMaxRssBytes": 100007936, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95895552, + "startupRssBytes": 65687552, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67055616, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 95895552, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 93995008, + "startupRssBytes": 65671168, + "loadedRssBytes": 67375104, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66916352, + "loadedPeakRssBytes": 68136960, + "resourceUsageMaxRssBytes": 93188096, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 89391104, + "median": 95895552, + "p95": 100007936, + "max": 100007936 + } + }, + "originalBfs": { + "samples": [ + { + "peakRssBytes": 121143296, + "startupRssBytes": 65421312, + "loadedRssBytes": 66732032, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66490368, + "loadedPeakRssBytes": 67485696, + "resourceUsageMaxRssBytes": 120414208, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120922112, + "startupRssBytes": 65372160, + "loadedRssBytes": 67207168, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 120160256, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120438784, + "startupRssBytes": 64557056, + "loadedRssBytes": 66392064, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65339392, + "loadedPeakRssBytes": 67448832, + "resourceUsageMaxRssBytes": 119697408, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 120311808, + "startupRssBytes": 65306624, + "loadedRssBytes": 67141632, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66830336, + "loadedPeakRssBytes": 68071424, + "resourceUsageMaxRssBytes": 119595008, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 121356288, + "startupRssBytes": 65691648, + "loadedRssBytes": 67395584, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 68116480, + "resourceUsageMaxRssBytes": 120606720, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 120311808, + "median": 120922112, + "p95": 121356288, + "max": 121356288 + } + }, + "upstream": { + "samples": [ + { + "peakRssBytes": 270356480, + "startupRssBytes": 65941504, + "loadedRssBytes": 68431872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67108864, + "loadedPeakRssBytes": 69251072, + "resourceUsageMaxRssBytes": 269815808, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270110720, + "startupRssBytes": 65540096, + "loadedRssBytes": 68685824, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 69443584, + "resourceUsageMaxRssBytes": 269246464, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 268992512, + "startupRssBytes": 64909312, + "loadedRssBytes": 68710400, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65966080, + "loadedPeakRssBytes": 69349376, + "resourceUsageMaxRssBytes": 267882496, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 309043200, + "startupRssBytes": 65306624, + "loadedRssBytes": 68452352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66768896, + "loadedPeakRssBytes": 69234688, + "resourceUsageMaxRssBytes": 309043200, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 270135296, + "startupRssBytes": 65089536, + "loadedRssBytes": 68497408, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65785856, + "loadedPeakRssBytes": 69300224, + "resourceUsageMaxRssBytes": 269443072, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 268992512, + "median": 270135296, + "p95": 309043200, + "max": 309043200 + } + }, + "baselineSelfA": { + "samples": [ + { + "peakRssBytes": 95563776, + "startupRssBytes": 65470464, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66633728, + "loadedPeakRssBytes": 68255744, + "resourceUsageMaxRssBytes": 95563776, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95862784, + "startupRssBytes": 65462272, + "loadedRssBytes": 67297280, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66928640, + "loadedPeakRssBytes": 68186112, + "resourceUsageMaxRssBytes": 95862784, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94965760, + "startupRssBytes": 65818624, + "loadedRssBytes": 67391488, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67014656, + "loadedPeakRssBytes": 68362240, + "resourceUsageMaxRssBytes": 94965760, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 95129600, + "startupRssBytes": 65208320, + "loadedRssBytes": 67305472, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66326528, + "loadedPeakRssBytes": 68149248, + "resourceUsageMaxRssBytes": 94162944, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94371840, + "startupRssBytes": 64974848, + "loadedRssBytes": 66940928, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66097152, + "loadedPeakRssBytes": 67719168, + "resourceUsageMaxRssBytes": 94371840, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 94371840, + "median": 95129600, + "p95": 95862784, + "max": 95862784 + } + }, + "baselineSelfB": { + "samples": [ + { + "peakRssBytes": 96808960, + "startupRssBytes": 64933888, + "loadedRssBytes": 67424256, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66068480, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 96731136, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94896128, + "startupRssBytes": 65703936, + "loadedRssBytes": 67407872, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 94896128, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 84250624, + "startupRssBytes": 65826816, + "loadedRssBytes": 67530752, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66949120, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 97107968, + "startupRssBytes": 65032192, + "loadedRssBytes": 66998272, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66183168, + "loadedPeakRssBytes": 67727360, + "resourceUsageMaxRssBytes": 97107968, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + { + "peakRssBytes": 94167040, + "startupRssBytes": 64937984, + "loadedRssBytes": 67428352, + "lockCount": 45, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66555904, + "loadedPeakRssBytes": 68247552, + "resourceUsageMaxRssBytes": 93442048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + ], + "peakRssBytes": { + "min": 84250624, + "median": 94896128, + "p95": 97107968, + "max": 97107968 + } + } + }, + "perLock": [ + { + "id": "ruined-tower-door", + "solvers": { + "candidate": { + "peakRssBytes": 77537280, + "startupRssBytes": 65085440, + "loadedRssBytes": 66658304, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66162688, + "loadedPeakRssBytes": 67620864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 74416128, + "startupRssBytes": 64368640, + "loadedRssBytes": 67383296, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65441792, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78462976, + "startupRssBytes": 65814528, + "loadedRssBytes": 67125248, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66789376, + "loadedPeakRssBytes": 67813376, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 106233856, + "startupRssBytes": 64303104, + "loadedRssBytes": 68497408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65495040, + "loadedPeakRssBytes": 69402624, + "resourceUsageMaxRssBytes": 105652224, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "scatty-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71573504, + "startupRssBytes": 65675264, + "loadedRssBytes": 67117056, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66748416, + "loadedPeakRssBytes": 67698688, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71282688, + "startupRssBytes": 65290240, + "loadedRssBytes": 67387392, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66768896, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78876672, + "startupRssBytes": 66211840, + "loadedRssBytes": 67260416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67403776, + "loadedPeakRssBytes": 68096000, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75640832, + "startupRssBytes": 65265664, + "loadedRssBytes": 68411392, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66207744, + "loadedPeakRssBytes": 69148672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "cor-galom-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72212480, + "startupRssBytes": 65150976, + "loadedRssBytes": 66592768, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66125824, + "loadedPeakRssBytes": 67354624, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71266304, + "startupRssBytes": 65617920, + "loadedRssBytes": 67452928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66957312, + "loadedPeakRssBytes": 68227072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 76562432, + "startupRssBytes": 65687552, + "loadedRssBytes": 67391488, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66940928, + "loadedPeakRssBytes": 68087808, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 73269248, + "startupRssBytes": 65531904, + "loadedRssBytes": 68415488, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67010560, + "loadedPeakRssBytes": 69345280, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "cor-galom-bedroom-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72265728, + "startupRssBytes": 65675264, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67072000, + "loadedPeakRssBytes": 68050944, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71311360, + "startupRssBytes": 65495040, + "loadedRssBytes": 67461120, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 68280320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78819328, + "startupRssBytes": 65101824, + "loadedRssBytes": 67198976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66678784, + "loadedPeakRssBytes": 68079616, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78999552, + "startupRssBytes": 65703936, + "loadedRssBytes": 68456448, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66990080, + "loadedPeakRssBytes": 69292032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "swamp-camp-forge-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72208384, + "startupRssBytes": 65228800, + "loadedRssBytes": 66670592, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66412544, + "loadedPeakRssBytes": 67661824, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71602176, + "startupRssBytes": 64950272, + "loadedRssBytes": 67571712, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65806336, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78815232, + "startupRssBytes": 65097728, + "loadedRssBytes": 67325952, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66359296, + "loadedPeakRssBytes": 68022272, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 83496960, + "startupRssBytes": 65830912, + "loadedRssBytes": 68714496, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67059712, + "loadedPeakRssBytes": 69357568, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "rice-lord-storeroom", + "solvers": { + "candidate": { + "peakRssBytes": 71999488, + "startupRssBytes": 65429504, + "loadedRssBytes": 66740224, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66564096, + "loadedPeakRssBytes": 67616768, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71303168, + "startupRssBytes": 65781760, + "loadedRssBytes": 67747840, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67104768, + "loadedPeakRssBytes": 68378624, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78467072, + "startupRssBytes": 65544192, + "loadedRssBytes": 66985984, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66797568, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75665408, + "startupRssBytes": 65040384, + "loadedRssBytes": 68448256, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65794048, + "loadedPeakRssBytes": 69210112, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-chambers-door", + "solvers": { + "candidate": { + "peakRssBytes": 72138752, + "startupRssBytes": 65363968, + "loadedRssBytes": 66936832, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66924544, + "loadedPeakRssBytes": 67854336, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71692288, + "startupRssBytes": 65499136, + "loadedRssBytes": 67203072, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66719744, + "loadedPeakRssBytes": 67993600, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79171584, + "startupRssBytes": 65282048, + "loadedRssBytes": 67248128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66682880, + "loadedPeakRssBytes": 68141056, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 80289792, + "startupRssBytes": 65380352, + "loadedRssBytes": 68395008, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67026944, + "loadedPeakRssBytes": 69320704, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-throne-hall-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71208960, + "startupRssBytes": 65286144, + "loadedRssBytes": 66727936, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66678784, + "loadedPeakRssBytes": 67715072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71991296, + "startupRssBytes": 65724416, + "loadedRssBytes": 67297280, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67112960, + "loadedPeakRssBytes": 68227072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 72019968, + "startupRssBytes": 65179648, + "loadedRssBytes": 67801088, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66400256, + "loadedPeakRssBytes": 68497408, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 72011776, + "startupRssBytes": 64962560, + "loadedRssBytes": 68632576, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66564096, + "loadedPeakRssBytes": 69357568, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "old-camp-dungeon-door", + "solvers": { + "candidate": { + "peakRssBytes": 77049856, + "startupRssBytes": 65257472, + "loadedRssBytes": 67092480, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66510848, + "loadedPeakRssBytes": 67887104, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 75661312, + "startupRssBytes": 65220608, + "loadedRssBytes": 67448832, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66314240, + "loadedPeakRssBytes": 68214784, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79208448, + "startupRssBytes": 63983616, + "loadedRssBytes": 66473984, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65093632, + "loadedPeakRssBytes": 67276800, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 85356544, + "startupRssBytes": 65171456, + "loadedRssBytes": 68448256, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66473984, + "loadedPeakRssBytes": 69226496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arlin-dungeon-key", + "solvers": { + "candidate": { + "peakRssBytes": 81268736, + "startupRssBytes": 65007616, + "loadedRssBytes": 66449408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66531328, + "loadedPeakRssBytes": 67469312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 79904768, + "startupRssBytes": 65724416, + "loadedRssBytes": 67428352, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66981888, + "loadedPeakRssBytes": 68276224, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 83525632, + "startupRssBytes": 64585728, + "loadedRssBytes": 67338240, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65556480, + "loadedPeakRssBytes": 68038656, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 76693504, + "startupRssBytes": 65495040, + "loadedRssBytes": 68509696, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66912256, + "loadedPeakRssBytes": 69320704, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-wine-cellar", + "solvers": { + "candidate": { + "peakRssBytes": 71606272, + "startupRssBytes": 65417216, + "loadedRssBytes": 66859008, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66609152, + "loadedPeakRssBytes": 67608576, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71225344, + "startupRssBytes": 65245184, + "loadedRssBytes": 67473408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66383872, + "loadedPeakRssBytes": 68194304, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79204352, + "startupRssBytes": 65544192, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66928640, + "loadedPeakRssBytes": 68059136, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75743232, + "startupRssBytes": 65306624, + "loadedRssBytes": 68583424, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 69271552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedside-chest", + "solvers": { + "candidate": { + "peakRssBytes": 76054528, + "startupRssBytes": 65183744, + "loadedRssBytes": 66887680, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66506752, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73519104, + "startupRssBytes": 64593920, + "loadedRssBytes": 67215360, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65310720, + "loadedPeakRssBytes": 67936256, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78987264, + "startupRssBytes": 64577536, + "loadedRssBytes": 67198976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65683456, + "loadedPeakRssBytes": 67956736, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78557184, + "startupRssBytes": 65847296, + "loadedRssBytes": 68599808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67043328, + "loadedPeakRssBytes": 69226496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-wall-chest", + "solvers": { + "candidate": { + "peakRssBytes": 79142912, + "startupRssBytes": 65122304, + "loadedRssBytes": 66957312, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66228224, + "loadedPeakRssBytes": 67846144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 76414976, + "startupRssBytes": 64720896, + "loadedRssBytes": 67080192, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65634304, + "loadedPeakRssBytes": 67944448, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79220736, + "startupRssBytes": 65282048, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66985984, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 111869952, + "startupRssBytes": 64249856, + "loadedRssBytes": 68444160, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65085440, + "loadedPeakRssBytes": 69144576, + "resourceUsageMaxRssBytes": 111263744, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "voran-bedside-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71966720, + "startupRssBytes": 65683456, + "loadedRssBytes": 66994176, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67055616, + "loadedPeakRssBytes": 67956736, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71983104, + "startupRssBytes": 65404928, + "loadedRssBytes": 67502080, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66428928, + "loadedPeakRssBytes": 68198400, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 83914752, + "startupRssBytes": 65208320, + "loadedRssBytes": 67436544, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66514944, + "loadedPeakRssBytes": 68112384, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 123584512, + "startupRssBytes": 65044480, + "loadedRssBytes": 68190208, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66375680, + "loadedPeakRssBytes": 69156864, + "resourceUsageMaxRssBytes": 122744832, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arto-room-door", + "solvers": { + "candidate": { + "peakRssBytes": 71913472, + "startupRssBytes": 65241088, + "loadedRssBytes": 66551808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66400256, + "loadedPeakRssBytes": 67444736, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70885376, + "startupRssBytes": 65437696, + "loadedRssBytes": 67272704, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66719744, + "loadedPeakRssBytes": 67952640, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78823424, + "startupRssBytes": 65675264, + "loadedRssBytes": 67248128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67026944, + "loadedPeakRssBytes": 68091904, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74354688, + "startupRssBytes": 65679360, + "loadedRssBytes": 68038656, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66564096, + "loadedPeakRssBytes": 68825088, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arto-far-bed-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72302592, + "startupRssBytes": 65015808, + "loadedRssBytes": 66326528, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66314240, + "loadedPeakRssBytes": 67268608, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71376896, + "startupRssBytes": 65216512, + "loadedRssBytes": 67575808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66478080, + "loadedPeakRssBytes": 68255744, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78929920, + "startupRssBytes": 65646592, + "loadedRssBytes": 67219456, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66936832, + "loadedPeakRssBytes": 68046848, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74788864, + "startupRssBytes": 65531904, + "loadedRssBytes": 68546560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66867200, + "loadedPeakRssBytes": 69308416, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "arto-near-bed-chest", + "solvers": { + "candidate": { + "peakRssBytes": 73064448, + "startupRssBytes": 64270336, + "loadedRssBytes": 65974272, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65445888, + "loadedPeakRssBytes": 66961408, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71794688, + "startupRssBytes": 64516096, + "loadedRssBytes": 66088960, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65302528, + "loadedPeakRssBytes": 66691072, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78839808, + "startupRssBytes": 65679360, + "loadedRssBytes": 67383296, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66977792, + "loadedPeakRssBytes": 68071424, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 110370816, + "startupRssBytes": 64880640, + "loadedRssBytes": 68419584, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66088960, + "loadedPeakRssBytes": 69353472, + "resourceUsageMaxRssBytes": 109670400, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedrooms-door", + "solvers": { + "candidate": { + "peakRssBytes": 79302656, + "startupRssBytes": 65355776, + "loadedRssBytes": 66928640, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66699264, + "loadedPeakRssBytes": 67833856, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 78962688, + "startupRssBytes": 65097728, + "loadedRssBytes": 67325952, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66465792, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79081472, + "startupRssBytes": 64839680, + "loadedRssBytes": 67067904, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65900544, + "loadedPeakRssBytes": 67969024, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 84283392, + "startupRssBytes": 64897024, + "loadedRssBytes": 68698112, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65888256, + "loadedPeakRssBytes": 69361664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedrooms-far-chest", + "solvers": { + "candidate": { + "peakRssBytes": 75325440, + "startupRssBytes": 65163264, + "loadedRssBytes": 66867200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66473984, + "loadedPeakRssBytes": 67735552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73637888, + "startupRssBytes": 65191936, + "loadedRssBytes": 67420160, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66863104, + "loadedPeakRssBytes": 68190208, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79294464, + "startupRssBytes": 67203072, + "loadedRssBytes": 67596288, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67887104, + "loadedPeakRssBytes": 68239360, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75583488, + "startupRssBytes": 65593344, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66969600, + "loadedPeakRssBytes": 69156864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gomez-bedrooms-near-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71790592, + "startupRssBytes": 64991232, + "loadedRssBytes": 66301952, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65740800, + "loadedPeakRssBytes": 67002368, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71270400, + "startupRssBytes": 64344064, + "loadedRssBytes": 67489792, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65359872, + "loadedPeakRssBytes": 68161536, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79159296, + "startupRssBytes": 64802816, + "loadedRssBytes": 67293184, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66056192, + "loadedPeakRssBytes": 68067328, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 85274624, + "startupRssBytes": 65421312, + "loadedRssBytes": 68435968, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 69005312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-left-chest", + "solvers": { + "candidate": { + "peakRssBytes": 75079680, + "startupRssBytes": 65523712, + "loadedRssBytes": 66965504, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66568192, + "loadedPeakRssBytes": 67747840, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73498624, + "startupRssBytes": 65802240, + "loadedRssBytes": 67244032, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66772992, + "loadedPeakRssBytes": 67981312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79056896, + "startupRssBytes": 64958464, + "loadedRssBytes": 67186688, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65679360, + "loadedPeakRssBytes": 68022272, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 92065792, + "startupRssBytes": 64827392, + "loadedRssBytes": 68497408, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66138112, + "loadedPeakRssBytes": 69206016, + "resourceUsageMaxRssBytes": 91205632, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-right-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72089600, + "startupRssBytes": 65638400, + "loadedRssBytes": 67211264, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67026944, + "loadedPeakRssBytes": 67960832, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72290304, + "startupRssBytes": 66674688, + "loadedRssBytes": 67592192, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67465216, + "loadedPeakRssBytes": 68386816, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79151104, + "startupRssBytes": 64765952, + "loadedRssBytes": 67256320, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65757184, + "loadedPeakRssBytes": 68038656, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 76038144, + "startupRssBytes": 65339392, + "loadedRssBytes": 68354048, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66514944, + "loadedPeakRssBytes": 69271552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor2-stairs-chest", + "solvers": { + "candidate": { + "peakRssBytes": 78045184, + "startupRssBytes": 64905216, + "loadedRssBytes": 66740224, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66195456, + "loadedPeakRssBytes": 67358720, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 75186176, + "startupRssBytes": 64901120, + "loadedRssBytes": 67260416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66289664, + "loadedPeakRssBytes": 68206592, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78721024, + "startupRssBytes": 64835584, + "loadedRssBytes": 67063808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66416640, + "loadedPeakRssBytes": 67735552, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 93245440, + "startupRssBytes": 65855488, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66990080, + "loadedPeakRssBytes": 69308416, + "resourceUsageMaxRssBytes": 92598272, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor2-chest-2", + "solvers": { + "candidate": { + "peakRssBytes": 72044544, + "startupRssBytes": 65884160, + "loadedRssBytes": 67063808, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67031040, + "loadedPeakRssBytes": 67891200, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72298496, + "startupRssBytes": 65290240, + "loadedRssBytes": 67256320, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66572288, + "loadedPeakRssBytes": 68222976, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78888960, + "startupRssBytes": 65081344, + "loadedRssBytes": 67178496, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66191360, + "loadedPeakRssBytes": 68038656, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74493952, + "startupRssBytes": 64372736, + "loadedRssBytes": 68567040, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65331200, + "loadedPeakRssBytes": 69373952, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor3-entrance-chest", + "solvers": { + "candidate": { + "peakRssBytes": 73695232, + "startupRssBytes": 65236992, + "loadedRssBytes": 66547712, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66519040, + "loadedPeakRssBytes": 67457024, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 73441280, + "startupRssBytes": 65343488, + "loadedRssBytes": 67702784, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66543616, + "loadedPeakRssBytes": 68182016, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78643200, + "startupRssBytes": 65867776, + "loadedRssBytes": 67047424, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66863104, + "loadedPeakRssBytes": 67829760, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 76640256, + "startupRssBytes": 65904640, + "loadedRssBytes": 68657152, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66973696, + "loadedPeakRssBytes": 69349376, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor3-empty-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71852032, + "startupRssBytes": 66080768, + "loadedRssBytes": 66998272, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67137536, + "loadedPeakRssBytes": 67940352, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71245824, + "startupRssBytes": 65163264, + "loadedRssBytes": 67260416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66428928, + "loadedPeakRssBytes": 68218880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78536704, + "startupRssBytes": 65486848, + "loadedRssBytes": 66928640, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66781184, + "loadedPeakRssBytes": 67825664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75935744, + "startupRssBytes": 64512000, + "loadedRssBytes": 68575232, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65622016, + "loadedPeakRssBytes": 69304320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor4-elixirs-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72155136, + "startupRssBytes": 64491520, + "loadedRssBytes": 66719744, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65703936, + "loadedPeakRssBytes": 67670016, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71299072, + "startupRssBytes": 65552384, + "loadedRssBytes": 67387392, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 82427904, + "startupRssBytes": 65687552, + "loadedRssBytes": 66736128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66732032, + "loadedPeakRssBytes": 67620864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78761984, + "startupRssBytes": 65454080, + "loadedRssBytes": 68730880, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 69373952, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-floor4-food-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72327168, + "startupRssBytes": 64372736, + "loadedRssBytes": 65421312, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65400832, + "loadedPeakRssBytes": 66535424, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71421952, + "startupRssBytes": 65310720, + "loadedRssBytes": 67407872, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66527232, + "loadedPeakRssBytes": 68280320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79224832, + "startupRssBytes": 65339392, + "loadedRssBytes": 67174400, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66560000, + "loadedPeakRssBytes": 68120576, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 93716480, + "startupRssBytes": 65572864, + "loadedRssBytes": 67801088, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66535424, + "loadedPeakRssBytes": 68571136, + "resourceUsageMaxRssBytes": 93716480, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "raven-tower-top-chest", + "solvers": { + "candidate": { + "peakRssBytes": 79269888, + "startupRssBytes": 66072576, + "loadedRssBytes": 67121152, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67076096, + "loadedPeakRssBytes": 67948544, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 76902400, + "startupRssBytes": 65351680, + "loadedRssBytes": 67710976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66949120, + "loadedPeakRssBytes": 68268032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78548992, + "startupRssBytes": 65146880, + "loadedRssBytes": 66981888, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66613248, + "loadedPeakRssBytes": 67817472, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 86102016, + "startupRssBytes": 65794048, + "loadedRssBytes": 68546560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66965504, + "loadedPeakRssBytes": 69246976, + "resourceUsageMaxRssBytes": 85250048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "fire-lizard-cave-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71999488, + "startupRssBytes": 65421312, + "loadedRssBytes": 67125248, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66793472, + "loadedPeakRssBytes": 67944448, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71417856, + "startupRssBytes": 64843776, + "loadedRssBytes": 67334144, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66064384, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79163392, + "startupRssBytes": 65540096, + "loadedRssBytes": 66850816, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66707456, + "loadedPeakRssBytes": 67694592, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 94773248, + "startupRssBytes": 65036288, + "loadedRssBytes": 68706304, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66277376, + "loadedPeakRssBytes": 69341184, + "resourceUsageMaxRssBytes": 94773248, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "silas-tavern-basement-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72122368, + "startupRssBytes": 64565248, + "loadedRssBytes": 66269184, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65400832, + "loadedPeakRssBytes": 66957312, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71405568, + "startupRssBytes": 65630208, + "loadedRssBytes": 67727360, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66318336, + "loadedPeakRssBytes": 68210688, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78974976, + "startupRssBytes": 65765376, + "loadedRssBytes": 67600384, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67031040, + "loadedPeakRssBytes": 68091904, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74752000, + "startupRssBytes": 65855488, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67010560, + "loadedPeakRssBytes": 69316608, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "silas-tavern-storeroom-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71946240, + "startupRssBytes": 66117632, + "loadedRssBytes": 67166208, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67235840, + "loadedPeakRssBytes": 67985408, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71139328, + "startupRssBytes": 65794048, + "loadedRssBytes": 67760128, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67022848, + "loadedPeakRssBytes": 68370432, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79196160, + "startupRssBytes": 65155072, + "loadedRssBytes": 67252224, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66383872, + "loadedPeakRssBytes": 68141056, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78635008, + "startupRssBytes": 64790528, + "loadedRssBytes": 68329472, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66011136, + "loadedPeakRssBytes": 69222400, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "silas-tavern-storeroom-door", + "solvers": { + "candidate": { + "peakRssBytes": 72241152, + "startupRssBytes": 64372736, + "loadedRssBytes": 65290240, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65490944, + "loadedPeakRssBytes": 66146304, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71512064, + "startupRssBytes": 64557056, + "loadedRssBytes": 67440640, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65273856, + "loadedPeakRssBytes": 68132864, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78893056, + "startupRssBytes": 65040384, + "loadedRssBytes": 67399680, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66592768, + "loadedPeakRssBytes": 68124672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75194368, + "startupRssBytes": 65142784, + "loadedRssBytes": 68419584, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66314240, + "loadedPeakRssBytes": 69242880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "jan-pond-house-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71491584, + "startupRssBytes": 65925120, + "loadedRssBytes": 66842624, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66760704, + "loadedPeakRssBytes": 67538944, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70500352, + "startupRssBytes": 65695744, + "loadedRssBytes": 67137536, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66633728, + "loadedPeakRssBytes": 67809280, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78970880, + "startupRssBytes": 65470464, + "loadedRssBytes": 67436544, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66777088, + "loadedPeakRssBytes": 68116480, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 93093888, + "startupRssBytes": 65413120, + "loadedRssBytes": 68034560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66576384, + "loadedPeakRssBytes": 68816896, + "resourceUsageMaxRssBytes": 91906048, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "graham-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72069120, + "startupRssBytes": 64765952, + "loadedRssBytes": 66338816, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65581056, + "loadedPeakRssBytes": 67194880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71299072, + "startupRssBytes": 65204224, + "loadedRssBytes": 67563520, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66428928, + "loadedPeakRssBytes": 68218880, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78872576, + "startupRssBytes": 65208320, + "loadedRssBytes": 67305472, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66457600, + "loadedPeakRssBytes": 68055040, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 79687680, + "startupRssBytes": 65462272, + "loadedRssBytes": 68476928, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66789376, + "loadedPeakRssBytes": 69304320, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "gravo-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72118272, + "startupRssBytes": 64983040, + "loadedRssBytes": 66686976, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66228224, + "loadedPeakRssBytes": 67579904, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70488064, + "startupRssBytes": 65179648, + "loadedRssBytes": 66883584, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66408448, + "loadedPeakRssBytes": 67633152, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79040512, + "startupRssBytes": 65675264, + "loadedRssBytes": 67117056, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66781184, + "loadedPeakRssBytes": 67846144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75771904, + "startupRssBytes": 65347584, + "loadedRssBytes": 68624384, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66322432, + "loadedPeakRssBytes": 69361664, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "diego-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72146944, + "startupRssBytes": 65880064, + "loadedRssBytes": 67190784, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67063808, + "loadedPeakRssBytes": 68005888, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72073216, + "startupRssBytes": 65470464, + "loadedRssBytes": 67567616, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66408448, + "loadedPeakRssBytes": 68186112, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78852096, + "startupRssBytes": 65925120, + "loadedRssBytes": 67366912, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67059712, + "loadedPeakRssBytes": 68120576, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78807040, + "startupRssBytes": 65200128, + "loadedRssBytes": 68345856, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66367488, + "loadedPeakRssBytes": 69296128, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "grim-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72241152, + "startupRssBytes": 65159168, + "loadedRssBytes": 66994176, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66891776, + "loadedPeakRssBytes": 67862528, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71405568, + "startupRssBytes": 65433600, + "loadedRssBytes": 67399680, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66494464, + "loadedPeakRssBytes": 68214784, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78860288, + "startupRssBytes": 65060864, + "loadedRssBytes": 67158016, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66310144, + "loadedPeakRssBytes": 68034560, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75022336, + "startupRssBytes": 65683456, + "loadedRssBytes": 68567040, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67006464, + "loadedPeakRssBytes": 69443584, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "mercenary-near-grim-chest", + "solvers": { + "candidate": { + "peakRssBytes": 71467008, + "startupRssBytes": 65024000, + "loadedRssBytes": 66072576, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66228224, + "loadedPeakRssBytes": 66879488, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71958528, + "startupRssBytes": 65830912, + "loadedRssBytes": 67403776, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66977792, + "loadedPeakRssBytes": 68313088, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78442496, + "startupRssBytes": 65724416, + "loadedRssBytes": 66904064, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66744320, + "loadedPeakRssBytes": 67756032, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 78852096, + "startupRssBytes": 64790528, + "loadedRssBytes": 68591616, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 65409024, + "loadedPeakRssBytes": 69369856, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "popular-lock", + "solvers": { + "candidate": { + "peakRssBytes": 72925184, + "startupRssBytes": 64864256, + "loadedRssBytes": 67223552, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66072576, + "loadedPeakRssBytes": 67854336, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 72818688, + "startupRssBytes": 65572864, + "loadedRssBytes": 67670016, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66842624, + "loadedPeakRssBytes": 68358144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78925824, + "startupRssBytes": 64843776, + "loadedRssBytes": 67465216, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66355200, + "loadedPeakRssBytes": 68124672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75907072, + "startupRssBytes": 65937408, + "loadedRssBytes": 67903488, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66772992, + "loadedPeakRssBytes": 68808704, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "alberto-mine-hut-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72032256, + "startupRssBytes": 65372160, + "loadedRssBytes": 66945024, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66519040, + "loadedPeakRssBytes": 67907584, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71299072, + "startupRssBytes": 66199552, + "loadedRssBytes": 67772416, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67170304, + "loadedPeakRssBytes": 68464640, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78888960, + "startupRssBytes": 65069056, + "loadedRssBytes": 67166208, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66940928, + "loadedPeakRssBytes": 68124672, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 75685888, + "startupRssBytes": 65064960, + "loadedRssBytes": 68472832, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66490368, + "loadedPeakRssBytes": 69316608, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "alberto-mine-upstairs-left-chest", + "solvers": { + "candidate": { + "peakRssBytes": 70963200, + "startupRssBytes": 65175552, + "loadedRssBytes": 67010560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66551808, + "loadedPeakRssBytes": 67665920, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71032832, + "startupRssBytes": 65646592, + "loadedRssBytes": 67481600, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66998272, + "loadedPeakRssBytes": 68358144, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 71507968, + "startupRssBytes": 65769472, + "loadedRssBytes": 67080192, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66801664, + "loadedPeakRssBytes": 67776512, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 72077312, + "startupRssBytes": 65044480, + "loadedRssBytes": 68321280, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66244608, + "loadedPeakRssBytes": 69312512, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "alberto-mine-upstairs-right-chest", + "solvers": { + "candidate": { + "peakRssBytes": 70955008, + "startupRssBytes": 65347584, + "loadedRssBytes": 66658304, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66600960, + "loadedPeakRssBytes": 67461120, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 70983680, + "startupRssBytes": 65761280, + "loadedRssBytes": 67334144, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66994176, + "loadedPeakRssBytes": 68202496, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 78843904, + "startupRssBytes": 65380352, + "loadedRssBytes": 67477504, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66445312, + "loadedPeakRssBytes": 68112384, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 73433088, + "startupRssBytes": 65761280, + "loadedRssBytes": 67727360, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66560000, + "loadedPeakRssBytes": 68489216, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "cavalorn-storeroom-door", + "solvers": { + "candidate": { + "peakRssBytes": 72126464, + "startupRssBytes": 65568768, + "loadedRssBytes": 67010560, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66990080, + "loadedPeakRssBytes": 67907584, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71397376, + "startupRssBytes": 65282048, + "loadedRssBytes": 67379200, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66547712, + "loadedPeakRssBytes": 68235264, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79187968, + "startupRssBytes": 65269760, + "loadedRssBytes": 67366912, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66609152, + "loadedPeakRssBytes": 68096000, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 85778432, + "startupRssBytes": 65150976, + "loadedRssBytes": 68558848, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66166784, + "loadedPeakRssBytes": 69103616, + "resourceUsageMaxRssBytes": 85106688, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + }, + { + "id": "old-to-new-camp-bandits-chest", + "solvers": { + "candidate": { + "peakRssBytes": 72785920, + "startupRssBytes": 65982464, + "loadedRssBytes": 67162112, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 67121152, + "loadedPeakRssBytes": 67997696, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "baseline": { + "peakRssBytes": 71737344, + "startupRssBytes": 65581056, + "loadedRssBytes": 67416064, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66609152, + "loadedPeakRssBytes": 68075520, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "originalBfs": { + "peakRssBytes": 79044608, + "startupRssBytes": 65212416, + "loadedRssBytes": 67309568, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66351104, + "loadedPeakRssBytes": 68112384, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + }, + "upstream": { + "peakRssBytes": 74752000, + "startupRssBytes": 65073152, + "loadedRssBytes": 68218880, + "lockCount": 1, + "peakRssMetric": "linux-proc-VmHWM", + "startupPeakRssBytes": 66187264, + "loadedPeakRssBytes": 69152768, + "resourceUsageMaxRssBytes": 84721664, + "startupResourceUsageMaxRssBytes": 84721664, + "resourceUsageLaunchFloorDetected": true + } + }, + "repetitions": 1, + "verdict": "inconclusive", + "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." + } + ] +} +``` + +## Environment and source provenance + +```json +{ + "environment": { + "node": "v24.19.0", + "v8": "13.6.233.17-node.51", + "platform": "linux", + "arch": "x64", + "cpu": "AMD EPYC 9V74 80-Core Processor", + "logicalCpus": 9, + "osRelease": "6.18.35", + "totalMemoryBytes": 23109910528, + "execArgv": [] + }, + "source": { + "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", + "dirty": false, + "files": { + "errors.ts": "830e0995bb1d5b98e8ce0495a22e5ae169cab10b2bdff2612e58323c397306f5", + "index.ts": "a1a2eedf5010cd66fc5d451035923e6ab417b36ba84cb2da85789cea17a1569a", + "indexed-heap.ts": "f776755f7405968b567922473c1e6ea5a63dae7643c4bd62b993159e900b7fbc", + "indexed.ts": "279b11d625a033f45723dd94df13a9be9fdfe267cff2535d5470b2011f11e125", + "lock-model.ts": "b4674921412e2acfbf0eaeff2031b3fc3907dffe886c42e588cd5ba6348ec966", + "matrix-analysis.ts": "a105dee650d645a730e3cc7e62a8e9faae31b29ad98c28aafce5ef15df236fc6", + "matrix-search.ts": "380ae745289a7ffd1997a6b9aa7dc3dd06a56e17453732d41042891c42435d3a", + "search-bfs.ts": "011c675973ccc8d753c0b9b3608882b16ca93ef4b460635c3474d8d5fc705feb", + "search-limits.ts": "91afc0940d49af46d9bb57d91af45df992f0684593d2f2d8bc3c13fad3f4c681", + "types.ts": "a2e72cf02e31694c52c1621cf5359863585b8383aa83a7d4e977c8547e8c879b" + }, + "harness": { + "catalog.ts": "2b75c9e9b5fe6997006307bf74cd90d8645d6d61fc527681f209c164680b880a", + "cli.ts": "b717c3d69c13d4a5b9a77589b1465f96338fe9d8be6501249f4470db035efe33", + "comparison.ts": "680ac76d266b51ff1f951fd3e232248bc5df0ab9eb70123285d31d176fb3a709", + "harness.ts": "14d23d697b2c4dd8adc13beac84b1cd3c0236aa34ae42ea96935fb30ac75e9f8", + "memory.ts": "429b45f541a2ac97e46fb6598ac30966cab4e2dca7e355c56f3ad3e037f43d93", + "rss.ts": "acc321bc34deac44819e5ff6d9af89e91c7394d9d897f7d69ce0cfedfc2e992c", + "solver.bench.ts": "985fbba3b387109261abeb1fdcbecd91b0ccfaa4dde5e0f162ad1758d529647f", + "validation.ts": "c44fc2a3de4da26ab207186a97a6b80ab20a910d27ae335da6cec536e402d77d" + }, + "scripts": { + "benchmark-compare.ts": "d4243c84a0a007b611e04e9c07031c3a7e48fb1a610366349084f0a7ab61cd71", + "benchmark-memory-worker.ts": "807707ca177f43c5ef08b88aa1068ad5f08f391b74f7f8d358159a2e5bf8a49b", + "benchmark-memory.ts": "c25a0407122f1c16261d8f8f9fd6f60cba463a7e288579ba67fde398db8d854f", + "benchmark-snapshot.ts": "50b31ffeac808f5b21f0fb864d2efd8a7c758491cf4e2d611bbe900bf3f9ec32", + "benchmark.ts": "fdb3f22dfdd495a6dde6a27fc3b0db68c7ceb75b94acaff131d30a25b6a06f24" + } + }, + "candidate": { + "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", + "moduleSha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b", + "format": "tuple", + "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/dist/gothic-lock-solver.mjs" + }, + "baseline": { + "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", + "moduleSha256": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "format": "legacy", + "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/reference-matrix/src/index.mjs", + "moduleTree": { + "cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", + "index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", + "matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", + "matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", + "result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", + "search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", + "search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", + "solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", + "state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", + "transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" + }, + "snapshotSha256": "c21b6319523f625439403c0417d267b5a63c35abafebef758bfc6c21217a3b94", + "snapshot": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/snapshots/reference-matrix.json", + "kind": "reference-baseline" + }, + "fixtures": { + "count": 45, + "catalogSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", + "manifestSha256": "a634d5328fd8d41470397b622ae0bd32b8aac0355dfc31975e023a63a62e65c9" + }, + "originalBfs": { + "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", + "revision": "a87e739a22ccc4215d6d9b14fa06b0738171cb60", + "license": "Apache-2.0", + "files": { + "src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", + "src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", + "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", + "src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", + "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", + "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", + "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" + }, + "notes": "Unmodified source snapshots; imports remain relative within this directory. No upstream UnlockMyLoot solver code is included." + }, + "matrixReference": { + "schemaVersion": 1, + "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", + "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", + "license": "Apache-2.0", + "files": { + "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "src/cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", + "src/index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", + "src/lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", + "src/matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", + "src/matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", + "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", + "src/search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", + "src/search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", + "src/solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", + "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", + "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" + }, + "notes": "Byte-identical Apache matrix reference, copied from source at the pinned revision. Benchmark-only; excluded from runtime and npm package. No external AGPL solver source." + }, + "upstream": { + "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", + "solver": { + "path": "index.html", + "sha256": "691f744099a9169f30e9883fddc5fdbb4949d306f3011e601678419d7b93821b", + "blockSha256": "d5bc85f40fd7c5b61097403df399ecb7dfd2b58924dc7a670aa0d05306017380", + "startMarker": "/* solver-start", + "endMarker": "/* solver-end */" + }, + "measured": true, + "externalCheckout": "/workspace/scratch/5d94b8672fa4/unlockmyloot", + "license": "External AGPL source is hash-verified and executed only from the supplied pinned checkout; no upstream solver code is vendored." + }, + "artifacts": { + "gothic-lock-solver.mjs": { + "bytes": 20143, + "sha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b" + }, + "gothic-lock-solver.min.mjs": { + "bytes": 9098, + "sha256": "9a7c0d365a0e2630372200f4c8a7914024b04afd048ca77e6a6783c120676d1f" + }, + "gothic-lock-solver.js": { + "bytes": 20916, + "sha256": "14841c4bc1563f4fd8beae3e277798878a2862efa0840e50b1aef9d5e5dff101" + }, + "gothic-lock-solver.min.js": { + "bytes": 9197, + "sha256": "2a936f190ae38f458e2b209681beb099974f32d654eb33be7249be6ede847694" + }, + "gothic-lock-solver.cli.mjs": { + "bytes": 23199, + "sha256": "75129dff219af243b05ffcbdb63ac1a14a32fd0d0b5ae8ee4528bb4d24b75f81" + } + } +} +``` + +## Interpretation + +Every warmup and measured solution is replayed independently one unit division at a time, checked against the stored exact action minimum, input immutability and deterministic paths. Target snapshot changes cannot mask candidate drift: the supplied baseline module must reproduce the supplied baseline snapshot. + +Historical upstream observations have a different objective (unit divisions, then switches); they are never a substitute for live paired timings. U/C/switches are descriptive and are still strict migration regression gates. Timings describe this Node runtime and machine only. A performance inconclusive verdict does not establish absence of regression. Sum of per-lock medians is not a whole-catalog wall-clock median. Raw samples, paths and calibration envelopes are in benchmark.json. diff --git a/docs/development.md b/docs/development.md index 1b0131b..6112ec2 100644 --- a/docs/development.md +++ b/docs/development.md @@ -124,3 +124,12 @@ from README and AGENTS instead of duplicated into another wiki. - [Vitest coverage](https://vitest.dev/guide/coverage.html) - [Playwright browsers](https://playwright.dev/docs/browsers) - [pnpm patched dependencies](https://pnpm.io/settings#patcheddependencies) + +## Independent migration review + +The [2026-09-07 review](reviews/2026-09-07-principles-review.md) records independent +checks of the mathematical contract, owner principles, strict TypeScript, +production consumers and release recovery. It distinguishes executed evidence +from the future first live npm/OIDC/CDN publication. The all-45 timing report +remains inconclusive about per-lock speed equivalence; no acceptance is inferred +from the absence of a confirmed aggregate regression. diff --git a/docs/releasing.md b/docs/releasing.md index 19d4a79..42fbb07 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -12,11 +12,13 @@ The package name was available during planning; that check does not reserve it. GitHub and npm account names need not match. Configure npm trusted publishing for the owner repository, `release.yml` workflow -and the release environment used by that workflow. Permit direct publication +and the `npm-production` release environment used by that workflow. Permit direct publication in the publisher settings where npm distinguishes staged from direct publishing. Use the [official npm instructions](https://docs.npmjs.com/trusted-publishers/). Do not put a token in chat or repository files. Once setup is complete, set the -repository readiness variable `NPM_PUBLICATION_READY=true`. +repository readiness variable `NPM_PUBLICATION_READY=true`. No npm token is +required by the OIDC workflow; GitHub supplies its own narrowly scoped +`GITHUB_TOKEN` for release operations. The automated release uses official npm CLI 12.0.2 with OIDC and provenance on a GitHub-hosted runner. GitHub assets are managed by official GitHub CLI 2.100.0. @@ -56,10 +58,14 @@ Both calibrated timing and whole-catalog peak-RSS comparisons must pass. An inconclusive or regressed performance result requires explicit review of that exact evidence, recorded by its SHA256 and a reason. This exception does not waive malformed paths, lost solvability, worse deterministic quality or mismatched -source bytes. Ordinary code changes cannot silently accept their own regression. +source bytes. The exceptional acceptance settings are repository variables +`PERFORMANCE_ACCEPTED_EVIDENCE_SHA256` and `PERFORMANCE_ACCEPTANCE_REASON`; +privileged steps read them independently of the uploaded artifact. Ordinary code changes cannot silently accept their own regression. ## Stable publication sequence +Each release-producing PR increments the SemVer in `package.json`; versions +are never inferred from arbitrary commit text or reused for changed files. After a future approved merge to main, the workflow validates the exact source commit and prepares one immutable candidate: diff --git a/docs/reviews/2026-09-07-principles-review.md b/docs/reviews/2026-09-07-principles-review.md new file mode 100644 index 0000000..7af1b2e --- /dev/null +++ b/docs/reviews/2026-09-07-principles-review.md @@ -0,0 +1,240 @@ +# Independent engineering review + +Date: 2026-09-07. Scope: the TypeScript library migration, its five distribution +files, tests, benchmark evidence, documentation, release helpers and subsequent +workflow integration. + +**Verdict: the reviewed local implementation passes the required checks.** Three +delivery findings were corrected and verified. Timing equivalence with the +preserved matrix implementation remains **inconclusive**, not a performance pass. +This review does not authorize publication or merging. + +## Reviewed source and evidence + +The measured runtime source is commit +`1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9`. The review also includes the subsequent +release-pagination correction and its regression test. After the independent +full build, all five executable-file hashes still matched the +[clean-source benchmark report](../benchmarks/run-20260907T091221367Z-d505e32e/benchmark.json). +Documentation and delivery-only changes do not change those measured runtime bytes. + +The reviewer independently read the owner's applicable public-safe engineering +principles, including proportional verification, invariant ownership, real +production-artifact checks and separation of deterministic and environmental +verdicts. Private wiki material is not reproduced here. + +## Principles applied to this implementation + +| Principle | Concrete evidence and assessment | +| --- | --- | +| DDD | `LockModel` owns validated state and directed links. `PreparedSearch` owns safe encoding and direct effects. Pin positions, numeric displacements, actions and search limits have explicit meanings. CLI/UI coordinates stay outside the mathematical model. | +| SOLID | Model validation, exact arithmetic, search budgets, priority queues, search strategies and file IO have distinct responsibilities. The public facade has two inputs. Internal strategies share a small contract; no public strategy registry is needed. | +| KISS / YAGNI | No DI container, repository layer, event bus, plugin mechanism or solver-instance wrapper was added. Stateful classes encapsulate actual state; rational operations remain functions. Vite builds the files; release helpers coordinate official CLIs and verified artifacts. | +| DRY | Production mechanics have one owner. CLI delegates to the core. The separate test simulator deliberately does not reuse production transitions: that independence can expose a shared implementation mistake. Preserved reference code is benchmark-only and excluded from the package. | +| BDD | Tests express observable results: signed asymmetric links, no cascade, blocked motion, nonmutation, already-open versus unreachable, distinct resource errors, installed imports and CLI exit codes. A Gherkin dependency would not strengthen these scenarios. | +| TDD | New contract and distribution tests were written before the corresponding entries/artifacts existed. The available historical red evidence has the limits stated below. Review findings received failing regression tests before fixes. | +| Evidence-driven verification | The reviewer executed the complete local production check, inspected the package-consumer matrix log, recomputed benchmark totals and verified report hashes. Author summaries alone were not treated as a passing gate. | + +The complexity and patterns are proportionate to an exact mathematical library +with multiple distribution environments. The review found no remaining concrete +mathematical correctness defect in the inspected implementation. + +## Mathematics and contract + +The implementation and [algorithm document](../algorithm.md) agree on +`M = I + linksᵀ`, direct noncascading effects and numeric pin displacement. +Endpoint bounds suffice because each affected position moves monotonically +within a command; independent replay nevertheless checks every unit shift. + +Exact rational elimination establishes necessary algebraic conditions. A +successful greedy certificate attains a lower bound; failed scheduling falls +through to search. For invertible M, an action changes one residual coordinate +by at most six, making `sum(ceil(abs(r[i])/6))` consistent. A* and the singular +case's unit-cost BFS therefore minimize grouped actions when they return a path. + +The documentation correctly avoids promising minimum unit shifts or distinct +selected plates. It separates the N≤18 safe-number representation limit from +the game's model, and distinguishes resource failure from proved impossibility. +Its complexity table includes exponential state space, dense initialization, +frontier storage and the distinction between rational-operation and bit costs. + +The 45 catalog cases are supported by independent unit-step replay and preserved +minimum-action results. All 441 two-plate configurations and 192 seeded +three-plate inputs are additionally checked against an independent exhaustive +oracle. These tests complement the optimality argument; their count is not a +proof over every larger input. + +## TypeScript and JavaScript review + +The pinned compiler is TypeScript 7.0.2. Its use was checked against the +[official TypeScript 7 announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/) +and current handbook guidance. The code uses ordinary JavaScript control flow +with explicit narrowing of unknown external data, as described in +[Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html). +There are no production `any` types, non-null assertions or unchecked type casts. + +Readonly tuples and mapped types express the known dimensions; runtime checks +handle dynamic arrays and JSON. Type-only imports, explicit module exports and +installed declaration-consumer tests agree with the +[module reference](https://www.typescriptlang.org/docs/handbook/modules/reference.html). +Strictness includes checked indexed access, exact optional properties and library +checking; see the [compiler option reference](https://www.typescriptlang.org/tsconfig/). + +Class fields with explicit constructor assignments remain current TypeScript. +Using every new syntax feature would not improve this contract; the +[classes handbook](https://www.typescriptlang.org/docs/handbook/2/classes.html) +supports the chosen composition of classes and functions. + +One compatibility exception is explicit: Vitest 5.0.0 needs a narrow declaration +patch under the selected strict checks. It corrects an absent type dependency +and optional-property unions, without changing runtime behavior or disabling +library checking. The rationale and removal condition are documented in +[development guidance](../development.md#strictness-and-the-dependency-patch). +This is not presented as an untouched upstream declaration package. + +## Independent local execution + +The reviewer ran the repository's actual `pnpm test` command, covering the entire +local pipeline, before GitHub workflow integration. A PTY capture preserved the +continuous output and final process status. The run exited **0**. + +| Gate | Observed result | +| --- | --- | +| Oxlint and strict TypeScript | Passed | +| Node behavior/coverage tests | 170 tests passed | +| Vite build | All five executable files built | +| Distribution tests | 13 tests passed | +| Vitest browser projects | 324 tests passed across Chromium, Firefox and WebKit | +| Static-file E2E | 24 scenarios passed, including all 45 locks for each of four core formats | +| Installed tarball | Type resolution, named/namespace imports and CLI exit codes 0/1/2 passed; all 24 browser scenarios passed again against installed package bytes | + +Browser versions were Chromium 153.0.8010.12, Firefox 155.0 and WebKit 26.6. +The reviewer directly ran Node 24.19.0. The separately captured package matrix +was inspected and showed the identical tarball passing Node 22.23.2 and 26.8.1 +consumer and CLI checks as well. + +Coverage includes all production `src/` and `cli/` sources, including unimported +files. The aggregate gate is 80% for each metric, not 80% per individual file. + +| Metric | Measured | Required | +| --- | ---: | ---: | +| Statements | 95.44% | 80% | +| Branches | 91.59% | 80% | +| Functions | 100% | 80% | +| Lines | 98.38% | 80% | + +The CLI entry's single top-level statement is exercised through subprocesses and +is still included as uncovered in the in-process coverage report. It was not +excluded to increase the percentage. The author also demonstrated a failing +100% threshold probe while its tests passed. Threshold semantics follow +[Vitest's coverage documentation](https://vitest.dev/config/coverage.html). + +Execution capture: `artifacts/verification/reviewer-full-test.log`, SHA-256 +`f588a5eee4a8c9aab920ed3560871f020b475b157b4336c32808aabacdc9d068`. +The verified npm tarball SHA-256 was +`cc9471e3958394d7dec04c17b1089b9849f675cf915601a88d5bb6a5ff883e19`. +These are evidence for this run, not permanent hashes for future releases. + +## TDD evidence and its limits + +The core author's recorded initial failure was an import of the missing +`src/index.ts`; a second probe called the new two-input contract against the +preserved old API and failed in its option validator. The second probe did not +reach its result assertion. Vitest was unavailable at that point, so a separate +Vitest red assertion was not captured for every new core test. + +Distribution testing recorded missing-file failures before the four bundles +existed, and a further missing-CLI failure before adding the fifth file. +Delivery review corrections recorded failures for semantic evidence tampering, +recovery and pagination before the corresponding fixes. This supports test-first +work, while falling short of a complete independently recorded red/green history +for every line of the migration. A passing final suite cannot reconstruct that +missing history, and this review does not claim otherwise. + +## Corrected review findings + +1. **Publishing-side evidence gate.** Prepared-artifact verification originally + checked hashes without rechecking benchmark semantics. It now validates + quality, performance acceptance, candidate source/module identity, linked + report hashes and agreement with the detailed report. Tests recompute the + manifest digest after changing evidence and still require rejection. Any + performance acceptance must come from the trusted caller for the exact + evidence hash; an artifact cannot approve itself. +2. **Older stable-release recovery.** Finalizing an older draft could promote it + above a newer stable release. Promotion now considers npm's latest version + and published stable versions; recovery tests retain the newer latest. +3. **Release-list pagination.** An existing release beyond the first 100 entries + could be mistaken for a missing release. The official `gh api --paginate + --slurp` result is validated and flattened before existence and promotion + decisions. A test places the release after 100 canaries and verifies recovery. + +Transport tests exercise absent versus unavailable registry responses, exact +existing tarball resumption, conflicting bytes, CDN MIME/CORS/hash rejection, +tag conflicts and draft versus published-asset behavior. They mock only external +transport/CLI boundaries and do not claim that npm publication happened locally. + +## Benchmark assessment and remaining limits + +The reviewer independently verified the JSON/Markdown report hashes, matching +evidence envelopes, 45 unique locks and all aggregate action metrics in the +[full report](../benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md). + +| Implementation | Actions A | Distinct plates U | Unit shifts C | +| --- | ---: | ---: | ---: | +| Typed candidate | 483 | 245 | 1723 | +| Preserved matrix implementation | 483 | 245 | 1723 | +| Original BFS | 483 | 245 | 1727 | +| Pinned Unlock My Loot | 491 | 245 | 1695 | + +Against the upstream objective, the candidate uses fewer actions on five locks +and ties on 40, with no action-count loss. It uses more unit shifts in aggregate; +the documentation must retain that tradeoff. + +Against the matrix reference, timing produced six passed and 39 inconclusive +per-lock verdicts, with no confirmed regression. This does **not** prove timing +equivalence. Both attempts of bounded repeats remain recorded. Memory passed its +calibrated comparison, but the measured Linux RSS high-water includes runtime +and harness allocation; it is not exact solver heap usage or browser memory. + +Live GitHub Actions execution, npm trusted-publisher/OIDC configuration and +post-publication CDN delivery require the future authorized release environment. +The local tests establish the executable preparation and verification paths; +they do not establish that an unpublished package already resolves through a CDN. +Workflow configuration was added and reviewed after this passing local gate, as +described below. No main merge, npm publication or upstream-project modification +was performed as part of this review. + +## Workflow integration review + +The independent full local run finished successfully before `.github/workflows/` +was introduced. The reviewer then inspected both workflows and Dependabot +configuration, parsed the YAML, checked every shell block with `bash -n` and +verified action pins and permission scopes, and independently ran standalone +actionlint 1.7.12. These checks passed; no third-party workflow action was added. + +- CI uses the proven local commands, preserves coverage/package/browser reports, + and compares the built candidate with a separate checkout of the actual PR + target SHA. A candidate snapshot cannot replace that target implementation. +- Each `uses:` entry belongs to GitHub's `actions` organization or pnpm's own + action, pinned to the reviewed complete commit SHA. Dependabot tracks Actions + and npm dependency updates. +- Release preparation produces one verified archive, measures it, saves immutable + raw inputs, then validates the saved evidence without rebuilding or remeasuring. + Job outputs identify the particular artifact needed for a failed-job retry. +- Candidate code runs in a job with read-only repository permissions. The npm + and GitHub publication jobs check out the trusted main workflow source and do + not install or execute candidate package dependencies. Only the npm job receives + OIDC token permission; only GitHub finalization receives repository write + permission. Checkout does not persist credentials. +- The publication order is npm, exact registry/CDN verification with real browser + consumers, then GitHub Release finalization. Partial failure keeps the existing + immutable version available for verified resumption. Canaries do not promote + stable tags, and older stable recovery does not replace a newer latest release. +- Inconclusive or regressed timing is not silently converted to a pass. Preparation + requires a passed comparison or an explicit trusted acceptance tied to the exact + saved evidence hash; quality regressions remain blocking. Initial publication + readiness is disabled until the owner configures npm trusted publishing. + +No new blocking workflow finding remained in this static review. Hosted execution +and live OIDC/CDN publication are separate evidence surfaces; YAML validation and +local mocked transport tests do not substitute for their eventual execution. diff --git a/scripts/release-network.ts b/scripts/release-network.ts index a6807d7..a3244d7 100644 --- a/scripts/release-network.ts +++ b/scripts/release-network.ts @@ -79,8 +79,12 @@ export async function completeGithubRelease(directory: string, manifest: Release object = record(record(annotated, 'annotated tag')['object'], 'tag target'); } if (object['type'] !== 'commit' || object['sha'] !== manifest.sourceSha) throw new Error('Existing release tag does not identify the prepared source'); - const releases: unknown = JSON.parse(gh(['api', `repos/${repository}/releases?per_page=100`])); - if (!Array.isArray(releases)) throw new Error('Invalid releases response'); + const pages: unknown = JSON.parse(gh(['api', `repos/${repository}/releases?per_page=100`, '--paginate', '--slurp'])); + if (!Array.isArray(pages)) throw new Error('Invalid release pages response'); + const releases: unknown[] = pages.flatMap((page: unknown): unknown[] => { + if (!Array.isArray(page)) throw new Error('Invalid releases page'); + return page; + }); const existing = releases.map(value => record(value, 'release')).find(value => value['tag_name'] === tag); if (!existing) { gh(['release', 'create', tag, '--repo', repository, '--verify-tag', '--target', manifest.sourceSha, '--draft', '--title', tag, diff --git a/tests/unit/release-network.test.ts b/tests/unit/release-network.test.ts index 3898917..1c597f3 100644 --- a/tests/unit/release-network.test.ts +++ b/tests/unit/release-network.test.ts @@ -73,7 +73,7 @@ describe('npm and CDN delivery boundaries', () => { }); }); -function mockGithub(directory: string, options: { draft: boolean; missing?: string; tagSha?: string; newer?: boolean }): void { +function mockGithub(directory: string, options: { draft: boolean; missing?: string; tagSha?: string; newer?: boolean; onSecondPage?: boolean }): void { const tag = `v${manifest.version}`; const filenames = [...releaseAssets, 'release-manifest.json']; const release = { tag_name: tag, draft: options.draft, prerelease: false, assets: filenames.filter(name => name !== options.missing).map(name => ({ name })) }; @@ -83,7 +83,11 @@ function mockGithub(directory: string, options: { draft: boolean; missing?: stri const endpoint = args[1] ?? ''; if (endpoint.includes('matching-refs')) return JSON.stringify([{ ref: `refs/tags/${tag}` }]); if (endpoint.includes('/git/ref/tags/')) return JSON.stringify({ object: { type: 'commit', sha: options.tagSha ?? manifest.sourceSha } }); - if (endpoint.includes('releases?')) return JSON.stringify([release, ...(options.newer ? [{ tag_name: 'v1.1.0', draft: false, prerelease: false }] : [])]); + if (endpoint.includes('releases?')) { + const firstPage = options.onSecondPage ? Array.from({ length: 100 }, (_, index) => ({ tag_name: `v2.0.0-canary.${index}`, draft: false, prerelease: true })) : [release]; + const pages = [firstPage, ...(options.onSecondPage ? [[release]] : []), ...(options.newer ? [[{ tag_name: 'v1.1.0', draft: false, prerelease: false }]] : [])]; + return JSON.stringify(args.includes('--paginate') && args.includes('--slurp') ? pages : firstPage); + } if (endpoint.includes('/releases/tags/')) return JSON.stringify(release); } if (args[0] === 'release' && args[1] === 'download') { @@ -138,4 +142,14 @@ describe('GitHub partial-release recovery', () => { expect(command.mock.calls.at(-1)?.[1]).toContain('--latest=false'); } finally { await rm(directory, { recursive: true, force: true }); } }); + + it('resumes an existing release beyond the first 100 entries without duplicate creation', async () => { + const directory = await releaseDirectory(); + try { + mockGithub(directory, { draft: true, onSecondPage: true, newer: true }); + await completeGithubRelease(directory, manifest, 'owner/repo'); + expect(command.mock.calls.some(([, args]) => args[0] === 'release' && args[1] === 'create')).toBe(false); + expect(command.mock.calls.at(-1)?.[1]).toContain('--latest=false'); + } finally { await rm(directory, { recursive: true, force: true }); } + }); }); From c6239ee21a6bd11e8c1385e3f20f7bff0b9bfa8f Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 7 Sep 2026 16:57:05 +0400 Subject: [PATCH 8/9] refactor: simplify library structure and expose solver configuration --- .codex/skills/gothic-lock-solver/SKILL.md | 45 - .../gothic-lock-solver/agents/openai.yaml | 4 - .gitattributes | 2 - {scripts => .github/scripts}/build.ts | 12 +- {scripts => .github/scripts}/ci-baseline.ts | 6 +- .../scripts}/release-browser.ts | 8 +- {scripts => .github/scripts}/release-files.ts | 2 +- {scripts => .github/scripts}/release-lib.ts | 0 .../scripts}/release-network.ts | 0 {scripts => .github/scripts}/release.ts | 2 +- .github/workflows/ci.yml | 17 +- .github/workflows/release.yml | 29 +- .oxlintrc.json | 26 +- AGENTS.md | 151 +- README.md | 210 +- benchmarks/README.md | 194 - benchmarks/baseline/LICENSE | 201 - benchmarks/baseline/README.md | 12 - benchmarks/baseline/manifest.json | 15 - benchmarks/baseline/src/index.mjs | 10 - benchmarks/baseline/src/lock-definition.mjs | 57 - benchmarks/baseline/src/result.mjs | 59 - benchmarks/baseline/src/solver.mjs | 80 - benchmarks/baseline/src/state-codec.mjs | 25 - benchmarks/baseline/src/transition.mjs | 46 - benchmarks/catalog.ts | 61 - benchmarks/cli.ts | 107 - benchmarks/fixtures/catalog.json | 9390 ------ benchmarks/fixtures/manifest.json | 115 - benchmarks/reference-matrix/LICENSE | 201 - benchmarks/reference-matrix/manifest.json | 21 - benchmarks/reference-matrix/src/cli.mjs | 36 - benchmarks/reference-matrix/src/index.mjs | 19 - .../reference-matrix/src/lock-definition.mjs | 60 - .../reference-matrix/src/matrix-analysis.mjs | 64 - .../reference-matrix/src/matrix-search.mjs | 167 - benchmarks/reference-matrix/src/result.mjs | 59 - .../reference-matrix/src/search-bfs.mjs | 86 - .../reference-matrix/src/search-limits.mjs | 116 - benchmarks/reference-matrix/src/solver.mjs | 25 - .../reference-matrix/src/state-codec.mjs | 25 - .../reference-matrix/src/transition.mjs | 46 - benchmarks/snapshots/reference-matrix.json | 2394 -- benchmarks/solver.bench.ts | 24 - cli/main.ts | 4 - docs/benchmarks/README.md | 76 - .../benchmark.json | 17137 ----------- .../benchmark.md | 239 - .../benchmark-evidence.json | 5953 ---- .../benchmark.json | 25093 ---------------- .../benchmark.md | 3858 --- docs/development.md | 135 - docs/reviews/2026-09-07-principles-review.md | 240 - .../plans/2026-08-31-gothic-lock-solver.md | 1430 - .../plans/2026-09-07-matrix-search.md | 64 - .../plans/2026-09-07-simplify-library.md | 161 + .../plans/2026-09-07-typescript-library.md | 108 - .../specs/2026-08-31-gothic-lock-solver.md | 360 - .../specs/2026-09-07-matrix-search.md | 38 - .../2026-09-07-typescript-library-design.md | 557 - docs/upstream-contribution.md | 25 - package.json | 15 +- patches/vitest@5.0.0.patch | 32 - playwright.config.ts | 3 +- pnpm-lock.yaml | 15 +- pnpm-workspace.yaml | 2 - scripts/benchmark-compare.ts | 2 - scripts/benchmark-memory.ts | 41 - scripts/benchmark-snapshot.ts | 29 - scripts/benchmark.ts | 2 - solve-lock.mjs | 4 - src/{matrix-search.ts => astar.ts} | 45 +- src/{search-bfs.ts => bfs.ts} | 10 +- {cli => src}/cli.ts | 75 +- src/config.ts | 48 + src/errors.ts | 2 +- src/index.ts | 15 +- src/indexed.ts | 24 - src/lock-model.ts | 58 - src/lock.ts | 163 + src/{matrix-analysis.ts => matrix.ts} | 4 +- src/{indexed-heap.ts => priority-queue.ts} | 0 src/search-limits.ts | 123 - src/types.ts | 4 - tests/benchmarks/benchmark.ts | 2 + tests/benchmarks/catalog.ts | 43 + tests/benchmarks/cli.ts | 93 + tests/benchmarks/compare.ts | 2 + .../benchmarks}/comparison.ts | 0 tests/benchmarks/fixtures.json | 2580 ++ {benchmarks => tests/benchmarks}/harness.ts | 64 +- .../benchmarks/memory-worker.ts | 18 +- {benchmarks => tests/benchmarks}/memory.ts | 4 +- {benchmarks => tests/benchmarks}/rss.ts | 0 tests/benchmarks/snapshot.ts | 46 + tests/benchmarks/source.ts | 105 + .../benchmarks}/validation.ts | 14 - tests/distribution/fixtures/consumer.mjs | 44 - .../distribution/fixtures/global-consumer.ts | 5 - tests/{types => e2e}/assets.d.ts | 0 tests/{ => e2e}/browser/catalog.test.ts | 12 +- tests/e2e/build-fixtures.ts | 40 + tests/e2e/catalog.ts | 10 +- tests/e2e/consumers.spec.ts | 18 +- .../{ => e2e}/distribution/artifacts.test.ts | 8 +- tests/{ => e2e}/distribution/catalog.test.ts | 4 +- tests/{ => e2e}/distribution/cli.test.ts | 10 +- .../distribution/fixtures/global-consumer.ts | 8 + .../distribution/fixtures/runtime-consumer.ts | 62 + .../distribution/fixtures/types-consumer.ts} | 20 +- tests/e2e/fixtures/classic.html | 10 +- tests/e2e/fixtures/classic.min.html | 10 +- tests/e2e/fixtures/classic.ts | 12 + .../e2e/fixtures}/lock.example.json | 0 tests/e2e/fixtures/module.html | 12 +- tests/e2e/fixtures/module.min.html | 12 +- tests/e2e/fixtures/module.ts | 13 + tests/e2e/fixtures/worker.min.mjs | 5 - tests/e2e/fixtures/worker.mjs | 5 - tests/e2e/fixtures/worker.ts | 9 + tests/e2e/server.ts | 10 +- {scripts => tests/e2e}/verify-package.ts | 18 +- tests/unit/benchmark.test.ts | 251 +- tests/unit/cli.test.ts | 91 +- tests/unit/config.test.ts | 133 + tests/unit/core-contract.test.ts | 7 +- .../core-contract.types.ts} | 2 +- tests/unit/core-limits.test.ts | 22 +- tests/unit/core-oracle.test.ts | 36 +- tests/{helpers => unit}/oracle.ts | 0 tests/unit/release-files.test.ts | 4 +- tests/unit/release-network.test.ts | 4 +- tests/unit/release.test.ts | 2 +- tsconfig.build.json | 13 +- tsconfig.json | 24 +- vitest.config.ts | 15 +- {docs => wiki}/algorithm.md | 17 +- {docs => wiki}/api.md | 29 +- wiki/benchmarks.md | 84 + wiki/configuration.md | 55 + wiki/development.md | 57 + {docs => wiki}/releasing.md | 10 +- 142 files changed, 4523 insertions(+), 70084 deletions(-) delete mode 100644 .codex/skills/gothic-lock-solver/SKILL.md delete mode 100644 .codex/skills/gothic-lock-solver/agents/openai.yaml delete mode 100644 .gitattributes rename {scripts => .github/scripts}/build.ts (73%) rename {scripts => .github/scripts}/ci-baseline.ts (92%) rename {scripts => .github/scripts}/release-browser.ts (89%) rename {scripts => .github/scripts}/release-files.ts (97%) rename {scripts => .github/scripts}/release-lib.ts (100%) rename {scripts => .github/scripts}/release-network.ts (100%) rename {scripts => .github/scripts}/release.ts (97%) delete mode 100644 benchmarks/README.md delete mode 100644 benchmarks/baseline/LICENSE delete mode 100644 benchmarks/baseline/README.md delete mode 100644 benchmarks/baseline/manifest.json delete mode 100644 benchmarks/baseline/src/index.mjs delete mode 100644 benchmarks/baseline/src/lock-definition.mjs delete mode 100644 benchmarks/baseline/src/result.mjs delete mode 100644 benchmarks/baseline/src/solver.mjs delete mode 100644 benchmarks/baseline/src/state-codec.mjs delete mode 100644 benchmarks/baseline/src/transition.mjs delete mode 100644 benchmarks/catalog.ts delete mode 100644 benchmarks/cli.ts delete mode 100644 benchmarks/fixtures/catalog.json delete mode 100644 benchmarks/fixtures/manifest.json delete mode 100644 benchmarks/reference-matrix/LICENSE delete mode 100644 benchmarks/reference-matrix/manifest.json delete mode 100644 benchmarks/reference-matrix/src/cli.mjs delete mode 100644 benchmarks/reference-matrix/src/index.mjs delete mode 100644 benchmarks/reference-matrix/src/lock-definition.mjs delete mode 100644 benchmarks/reference-matrix/src/matrix-analysis.mjs delete mode 100644 benchmarks/reference-matrix/src/matrix-search.mjs delete mode 100644 benchmarks/reference-matrix/src/result.mjs delete mode 100644 benchmarks/reference-matrix/src/search-bfs.mjs delete mode 100644 benchmarks/reference-matrix/src/search-limits.mjs delete mode 100644 benchmarks/reference-matrix/src/solver.mjs delete mode 100644 benchmarks/reference-matrix/src/state-codec.mjs delete mode 100644 benchmarks/reference-matrix/src/transition.mjs delete mode 100644 benchmarks/snapshots/reference-matrix.json delete mode 100644 benchmarks/solver.bench.ts delete mode 100644 cli/main.ts delete mode 100644 docs/benchmarks/README.md delete mode 100644 docs/benchmarks/run-20260907T070258001Z-f78dff13/benchmark.json delete mode 100644 docs/benchmarks/run-20260907T070258001Z-f78dff13/benchmark.md delete mode 100644 docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark-evidence.json delete mode 100644 docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.json delete mode 100644 docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md delete mode 100644 docs/development.md delete mode 100644 docs/reviews/2026-09-07-principles-review.md delete mode 100644 docs/superpowers/plans/2026-08-31-gothic-lock-solver.md delete mode 100644 docs/superpowers/plans/2026-09-07-matrix-search.md create mode 100644 docs/superpowers/plans/2026-09-07-simplify-library.md delete mode 100644 docs/superpowers/plans/2026-09-07-typescript-library.md delete mode 100644 docs/superpowers/specs/2026-08-31-gothic-lock-solver.md delete mode 100644 docs/superpowers/specs/2026-09-07-matrix-search.md delete mode 100644 docs/superpowers/specs/2026-09-07-typescript-library-design.md delete mode 100644 docs/upstream-contribution.md delete mode 100644 patches/vitest@5.0.0.patch delete mode 100644 pnpm-workspace.yaml delete mode 100644 scripts/benchmark-compare.ts delete mode 100644 scripts/benchmark-memory.ts delete mode 100644 scripts/benchmark-snapshot.ts delete mode 100644 scripts/benchmark.ts delete mode 100755 solve-lock.mjs rename src/{matrix-search.ts => astar.ts} (82%) rename src/{search-bfs.ts => bfs.ts} (92%) rename {cli => src}/cli.ts (58%) create mode 100644 src/config.ts delete mode 100644 src/indexed.ts delete mode 100644 src/lock-model.ts create mode 100644 src/lock.ts rename src/{matrix-analysis.ts => matrix.ts} (97%) rename src/{indexed-heap.ts => priority-queue.ts} (100%) delete mode 100644 src/search-limits.ts create mode 100644 tests/benchmarks/benchmark.ts create mode 100644 tests/benchmarks/catalog.ts create mode 100644 tests/benchmarks/cli.ts create mode 100644 tests/benchmarks/compare.ts rename {benchmarks => tests/benchmarks}/comparison.ts (100%) create mode 100644 tests/benchmarks/fixtures.json rename {benchmarks => tests/benchmarks}/harness.ts (79%) rename scripts/benchmark-memory-worker.ts => tests/benchmarks/memory-worker.ts (60%) rename {benchmarks => tests/benchmarks}/memory.ts (98%) rename {benchmarks => tests/benchmarks}/rss.ts (100%) create mode 100644 tests/benchmarks/snapshot.ts create mode 100644 tests/benchmarks/source.ts rename {benchmarks => tests/benchmarks}/validation.ts (83%) delete mode 100644 tests/distribution/fixtures/consumer.mjs delete mode 100644 tests/distribution/fixtures/global-consumer.ts rename tests/{types => e2e}/assets.d.ts (100%) rename tests/{ => e2e}/browser/catalog.test.ts (71%) create mode 100644 tests/e2e/build-fixtures.ts rename tests/{ => e2e}/distribution/artifacts.test.ts (86%) rename tests/{ => e2e}/distribution/catalog.test.ts (74%) rename tests/{ => e2e}/distribution/cli.test.ts (78%) create mode 100644 tests/e2e/distribution/fixtures/global-consumer.ts create mode 100644 tests/e2e/distribution/fixtures/runtime-consumer.ts rename tests/{distribution/fixtures/consumer.mts => e2e/distribution/fixtures/types-consumer.ts} (64%) create mode 100644 tests/e2e/fixtures/classic.ts rename {examples => tests/e2e/fixtures}/lock.example.json (100%) create mode 100644 tests/e2e/fixtures/module.ts delete mode 100644 tests/e2e/fixtures/worker.min.mjs delete mode 100644 tests/e2e/fixtures/worker.mjs create mode 100644 tests/e2e/fixtures/worker.ts rename {scripts => tests/e2e}/verify-package.ts (82%) create mode 100644 tests/unit/config.test.ts rename tests/{types/core-contract.ts => unit/core-contract.types.ts} (95%) rename tests/{helpers => unit}/oracle.ts (100%) rename {docs => wiki}/algorithm.md (91%) rename {docs => wiki}/api.md (82%) create mode 100644 wiki/benchmarks.md create mode 100644 wiki/configuration.md create mode 100644 wiki/development.md rename {docs => wiki}/releasing.md (95%) diff --git a/.codex/skills/gothic-lock-solver/SKILL.md b/.codex/skills/gothic-lock-solver/SKILL.md deleted file mode 100644 index a3d5792..0000000 --- a/.codex/skills/gothic-lock-solver/SKILL.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: gothic-lock-solver -description: Use when a user provides Gothic 1 Remake lock-plate positions or links, or requests a shortest combination; not for ordinary code locks or cases requiring guessed unconfirmed links. ---- - -# Gothic Lock Solver - -Используй вычислительное ядро проекта, а не языковую модель, для поиска комбинации. - -## Правила модели - -- Пластин от 2 до 7, позиции каждой от 1 до 7, цель всегда 4. -- Влево означает `+1`, вправо означает `-1`. -- Каждая строка матрицы направленная и независимая; обратную связь не подразумевай. -- Связи не каскадируют. Диагональ матрицы равна 0. -- Если зависимая пластина не может выполнить весь сдвиг, команда заблокирована полностью. -- Не используй приложенный PDF в этом workflow. - -## Сбор входа - -1. Зафиксируй число пластин и текущий вектор слева направо. -2. Для каждой исходной пластины отдельно зафиксируй, какие другие пластины движутся вместе с ней. -3. Запиши `1` для того же направления, `-1` для противоположного, `0` только для подтвержденного отсутствия связи. -4. Не запускай решатель, пока в каждой строке не устранены неизвестные связи. -5. Создай JSON с полями `state` и `links` по схеме `gothic-lock-solver-spec.md`. - -## Запуск - -Разреши корень проекта как три уровня вверх от каталога этого skill. Запусти: - -```bash -node /solve-lock.mjs -``` - -Добавляй `--output ` только когда пользователь просит файл результата. - -Передай пользователю команды без перегруппировки и с исходной нумерацией пластин: - -```text -Пластина K - влево|вправо xS -``` - -## Проверка в игре - -После каждой выполненной команды сверяй наблюдаемое состояние с рассчитанным, если пользователь сообщает его. При первом расхождении останови текущую комбинацию, зафиксируй фактический вектор и уточни только затронутую строку матрицы. Не продолжай по заведомо неверной модели. diff --git a/.codex/skills/gothic-lock-solver/agents/openai.yaml b/.codex/skills/gothic-lock-solver/agents/openai.yaml deleted file mode 100644 index 64d5729..0000000 --- a/.codex/skills/gothic-lock-solver/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Gothic Lock Solver" - short_description: "Собирает модель замка и находит кратчайшую комбинацию" - default_prompt: "Помоги описать и решить замок Gothic 1 Remake." diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 2dab4a1..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Unified diff context uses a leading space before the original tab indentation. -patches/*.patch whitespace=-space-before-tab diff --git a/scripts/build.ts b/.github/scripts/build.ts similarity index 73% rename from scripts/build.ts rename to .github/scripts/build.ts index f7435e4..37c61a3 100644 --- a/scripts/build.ts +++ b/.github/scripts/build.ts @@ -5,27 +5,27 @@ import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; import { build } from 'vite'; -const root = fileURLToPath(new URL('../', import.meta.url)); -const dist = new URL('../dist/', import.meta.url); +const root = fileURLToPath(new URL('../../', import.meta.url)); +const dist = new URL('../../dist/', import.meta.url); await rm(dist, { recursive: true, force: true }); await mkdir(dist, { recursive: true }); for (const mode of ['readable', 'minified']) { - await build({ root, configFile: fileURLToPath(new URL('../vite.config.ts', import.meta.url)), mode }); + await build({ root, configFile: fileURLToPath(new URL('../../vite.config.ts', import.meta.url)), mode }); } await build({ root, configFile: false, build: { target: 'node22', outDir: 'dist', emptyOutDir: false, minify: false, sourcemap: false, - lib: { entry: fileURLToPath(new URL('../cli/main.ts', import.meta.url)), formats: ['es'], fileName: () => 'gothic-lock-solver.cli.mjs' }, + lib: { entry: fileURLToPath(new URL('../../src/cli.ts', import.meta.url)), formats: ['es'], fileName: () => 'gothic-lock-solver.cli.mjs' }, rolldownOptions: { external: [...builtinModules, ...builtinModules.map((name) => `node:${name}`)], output: { codeSplitting: false, comments: { legal: false } } }, }, }); -execFileSync(process.execPath, [fileURLToPath(new URL('../node_modules/typescript/bin/tsc', import.meta.url)), '-p', 'tsconfig.build.json'], { cwd: root, stdio: 'inherit' }); +execFileSync(process.execPath, [fileURLToPath(new URL('../../node_modules/typescript/bin/tsc', import.meta.url)), '-p', 'tsconfig.build.json'], { cwd: root, stdio: 'inherit' }); await writeFile(new URL('global.d.ts', dist), `/** Types for the classic GothicLockSolver global. */\nexport as namespace GothicLockSolver;\nexport * from './index.js';\n`); const files: Record = {}; for (const filename of (await readdir(dist)).sort()) { files[filename] = createHash('sha256').update(await readFile(new URL(filename, dist))).digest('hex'); } -const metadata: unknown = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')); +const metadata: unknown = JSON.parse(await readFile(new URL('../../package.json', import.meta.url), 'utf8')); if (typeof metadata !== 'object' || metadata === null || !('version' in metadata) || typeof metadata.version !== 'string') throw new Error('Package version is missing.'); const sourceSha = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim(); await writeFile(new URL('manifest.json', dist), `${JSON.stringify({ schemaVersion: 1, package: 'gothic-lock-solver', version: metadata.version, sourceSha, files }, null, 2)}\n`); diff --git a/scripts/ci-baseline.ts b/.github/scripts/ci-baseline.ts similarity index 92% rename from scripts/ci-baseline.ts rename to .github/scripts/ci-baseline.ts index 07758bb..8673b31 100644 --- a/scripts/ci-baseline.ts +++ b/.github/scripts/ci-baseline.ts @@ -6,7 +6,7 @@ import { writeJson } from './release-files.ts'; import { fetchBytes, verifyRegistry } from './release-network.ts'; const [mode, directoryArgument, outputArgument] = process.argv.slice(2); -if (!['checkout', 'release'].includes(mode ?? '') || !directoryArgument || !outputArgument) throw new Error('Usage: node scripts/ci-baseline.ts checkout BASE_CHECKOUT OUTPUT_DIR | release CANDIDATE_VERSION OUTPUT_DIR'); +if (!['checkout', 'release'].includes(mode ?? '') || !directoryArgument || !outputArgument) throw new Error('Usage: node .github/scripts/ci-baseline.ts checkout BASE_CHECKOUT OUTPUT_DIR | release CANDIDATE_VERSION OUTPUT_DIR'); const output = resolve(outputArgument); await mkdir(output, { recursive: true }); @@ -24,7 +24,7 @@ if (mode === 'checkout') { const format = legacy ? 'legacy' : 'tuple'; const snapshot = join(output, 'baseline-snapshot.json'); // The supplied base checkout, never a candidate-owned expected snapshot, supplies the live baseline. - execFileSync(process.execPath, ['scripts/benchmark-snapshot.ts', '--source-module', module, '--source-format', format, + execFileSync(process.execPath, ['tests/benchmarks/snapshot.ts', '--source-module', module, '--source-format', format, '--reason', 'Fresh snapshot from the separately checked-out PR target/reference commit; candidate snapshot is not read.', '--output', snapshot], { stdio: 'inherit' }); await emit({ available: 'true', module, snapshot, format }); } else { @@ -58,7 +58,7 @@ if (mode === 'checkout') { await writeFile(module, moduleBytes, { flag: 'wx' }); await writeFile(join(output, 'previous-release-manifest.json'), manifestBytes, { flag: 'wx' }); await writeFile(join(output, 'previous-benchmark-evidence.json'), evidenceBytes, { flag: 'wx' }); - await writeJson(snapshotPath, { ...snapshot, source: { ...source, releaseVersion: previous, kind: 'previous-stable-release' } }); + await writeJson(snapshotPath, { ...snapshot, source: { ...source, releaseVersion: previous, kind: 'previous-stable-release', releaseManifestSha256: sha256(manifestBytes), releaseEvidenceSha256: sha256(evidenceBytes) } }); // Re-read the bytes that will actually be imported, after writing the evidence chain. assertDigest(await readFile(module), string(source['moduleSha256'], 'snapshot module hash'), 'saved baseline module'); await emit({ available: 'true', module, snapshot: snapshotPath, format: 'tuple', version: previous }); diff --git a/scripts/release-browser.ts b/.github/scripts/release-browser.ts similarity index 89% rename from scripts/release-browser.ts rename to .github/scripts/release-browser.ts index 44db6ec..874522c 100644 --- a/scripts/release-browser.ts +++ b/.github/scripts/release-browser.ts @@ -1,7 +1,7 @@ import { createServer } from 'node:http'; import { chromium, firefox, webkit } from 'playwright'; -import { loadCatalog } from '../benchmarks/catalog.ts'; -import { replayCommands, tupleCommands } from '../benchmarks/validation.ts'; +import { loadCatalog } from '../../tests/benchmarks/catalog.ts'; +import { replayCommands, tupleCommands } from '../../tests/benchmarks/validation.ts'; import { runtimeFiles, type ReleaseManifest } from './release-lib.ts'; /** Real CDN imports, in each supported engine, after byte/header verification. */ @@ -31,13 +31,13 @@ export async function verifyCdnBrowsers(manifest: ReleaseManifest): Promise Reflect.apply(solve, undefined, [definition.state, definition.links])); - }, { moduleUrl: url, moduleFormat: filename.endsWith('.mjs'), definitions: locks.map(lock => lock.definition) }); + }, { moduleUrl: url, moduleFormat: filename.endsWith('.mjs'), definitions: locks.map(({ state, links }) => ({ state, links })) }); if (!Array.isArray(results) || results.length !== 45) throw new Error('CDN fixture results are incomplete'); for (const [index, result] of results.entries()) { const lock = locks[index]; if (!lock) throw new Error('Unexpected CDN fixture'); const commands = tupleCommands(result); - if (commands.length !== lock.expectedActions || replayCommands(lock.definition, commands).some(position => position !== 4)) throw new Error(`CDN fixture mismatch: ${engine.name()}/${filename}/${lock.id}`); + if (commands.length !== lock.expectedActions || replayCommands(lock, commands).some(position => position !== 4)) throw new Error(`CDN fixture mismatch: ${engine.name()}/${filename}/${lock.id}`); } if (runtimeRequests.length !== 1 || runtimeRequests[0] !== url) throw new Error(`Additional CDN runtime dependency: ${runtimeRequests.join(', ')}`); console.log(`CDN browser passed: ${engine.name()}, ${filename}, 45 fixtures`); diff --git a/scripts/release-files.ts b/.github/scripts/release-files.ts similarity index 97% rename from scripts/release-files.ts rename to .github/scripts/release-files.ts index 84a6688..8c7165e 100644 --- a/scripts/release-files.ts +++ b/.github/scripts/release-files.ts @@ -105,7 +105,7 @@ export async function prepareRelease(options: PrepareOptions): Promise> "$GITHUB_OUTPUT" echo "ref=refs/heads/$SOURCE_BRANCH" >> "$GITHUB_OUTPUT" - - name: Check out pinned external upstream - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - repository: 1h8s/unlockmyloot - ref: eee0bf50ebcb7fffd2b47954fd76bb015854e365 - path: upstream - persist-credentials: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 22.23.2 @@ -100,7 +93,7 @@ jobs: if: env.RELEASE_KIND == 'canary' env: RELEASE_SOURCE_SHA: ${{ steps.source.outputs.sha }} - run: node ../workflow/scripts/release.ts canary-version + run: node ../workflow/.github/scripts/release.ts canary-version - name: Install all actual browsers run: pnpm exec playwright install --with-deps chromium firefox webkit - name: Check source, coverage, all formats and the one packed archive @@ -120,7 +113,7 @@ jobs: id: previous run: | version=$(node -p "JSON.parse(require('node:fs').readFileSync('package.json','utf8')).version") - node ../workflow/scripts/ci-baseline.ts release "$version" artifacts/baseline + node ../workflow/.github/scripts/ci-baseline.ts release "$version" artifacts/baseline - name: Check out the explicit first-release reference when needed if: steps.previous.outputs.available != 'true' uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -131,7 +124,7 @@ jobs: - name: Generate a snapshot from the pinned first-release checkout id: reference if: steps.previous.outputs.available != 'true' - run: node ../workflow/scripts/ci-baseline.ts checkout ../reference-matrix artifacts/reference-baseline + run: node ../workflow/.github/scripts/ci-baseline.ts checkout ../reference-matrix artifacts/reference-baseline - name: Measure 45 fixtures against previous stable or explicit reference id: comparison # Keep the measured artifact available. The separate prepare job enforces @@ -141,7 +134,7 @@ jobs: BASELINE_MODULE: ${{ steps.previous.outputs.module || steps.reference.outputs.module }} BASELINE_SNAPSHOT: ${{ steps.previous.outputs.snapshot || steps.reference.outputs.snapshot }} BASELINE_FORMAT: ${{ steps.previous.outputs.format || steps.reference.outputs.format }} - run: pnpm benchmark:compare --candidate-module dist/gothic-lock-solver.mjs --baseline-module "$BASELINE_MODULE" --baseline-snapshot "$BASELINE_SNAPSHOT" --baseline-format "$BASELINE_FORMAT" --reference ../upstream --output-dir artifacts/benchmark + run: pnpm benchmark:compare --candidate-module dist/gothic-lock-solver.mjs --baseline-module "$BASELINE_MODULE" --baseline-snapshot "$BASELINE_SNAPSHOT" --baseline-format "$BASELINE_FORMAT" --output-dir artifacts/benchmark - name: Require one complete evidence set id: report run: | @@ -184,9 +177,9 @@ jobs: with: name: release-checks-${{ github.run_id }}-${{ github.run_attempt }} path: | - candidate/coverage/ - candidate/playwright-report/ - candidate/test-results/ + candidate/artifacts/coverage/ + candidate/artifacts/playwright-report/ + candidate/artifacts/test-results/ candidate/artifacts/benchmark/ if-no-files-found: warn retention-days: 30 @@ -218,7 +211,7 @@ jobs: env: SOURCE_SHA: ${{ needs.build.outputs.source-sha }} BENCHMARK_DIRECTORY: ${{ needs.build.outputs.benchmark-directory }} - run: node ../workflow/scripts/release.ts prepare --benchmark-dir "$BENCHMARK_DIRECTORY" --source-sha "$SOURCE_SHA" + run: node ../workflow/.github/scripts/release.ts prepare --benchmark-dir "$BENCHMARK_DIRECTORY" --source-sha "$SOURCE_SHA" - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: release-prepared-${{ github.run_id }}-${{ github.run_attempt }} @@ -254,7 +247,7 @@ jobs: with: name: ${{ needs.prepare.outputs.artifact }} path: artifacts/release - - run: node scripts/release.ts publish + - run: node .github/scripts/release.ts publish - name: Preserve partial-publication status if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -290,7 +283,7 @@ jobs: with: name: ${{ needs.prepare.outputs.artifact }} path: artifacts/release - - run: node scripts/release.ts verify + - run: node .github/scripts/release.ts verify - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: release-verified-${{ github.run_id }}-${{ github.run_attempt }} @@ -333,7 +326,7 @@ jobs: - name: Verify existing assets or finalize the complete draft env: GH_TOKEN: ${{ github.token }} - run: node scripts/release.ts github + run: node .github/scripts/release.ts github - name: Preserve completion or recovery status if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 diff --git a/.oxlintrc.json b/.oxlintrc.json index 38e255e..c2164ba 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -1,11 +1,29 @@ { "$schema": "./node_modules/oxlint/configuration_schema.json", - "plugins": ["typescript", "unicorn", "oxc"], - "categories": { "correctness": "error" }, + "plugins": [ + "typescript", + "unicorn", + "oxc" + ], + "categories": { + "correctness": "error" + }, "rules": { "typescript/no-explicit-any": "error", "typescript/no-non-null-assertion": "error", - "typescript/ban-ts-comment": ["error", { "ts-ignore": true, "ts-nocheck": true, "ts-expect-error": "allow-with-description" }] + "typescript/ban-ts-comment": [ + "error", + { + "ts-ignore": true, + "ts-nocheck": true, + "ts-expect-error": "allow-with-description" + } + ] }, - "ignorePatterns": ["**/*.mjs", "**/*.js", "dist/**", "node_modules/**", "tests/distribution/fixtures/**"] + "ignorePatterns": [ + "dist/**", + "node_modules/**", + "artifacts/**", + "tests/e2e/distribution/fixtures/**" + ] } diff --git a/AGENTS.md b/AGENTS.md index 4f2160b..1cacada 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,93 +1,62 @@ # Agent instructions -## Purpose and authority - -Gothic Lock Solver is a strict TypeScript mathematical library and standalone -Node.js CLI. The active requirements are in -[the library specification](docs/superpowers/specs/2026-09-07-typescript-library-design.md). -The owner approved a migration from the preserved JavaScript reference; old -specifications and reports are historical, not the active module contract. - -Read [README](README.md), [API](docs/api.md), [algorithm](docs/algorithm.md), -[development](docs/development.md) and relevant tests before changing code. -Public documentation must be self-contained; access to private owner notes is -not a development prerequisite. Do not copy private notes into this repository. - -## Invariants - -- At least two plates; numeric pin positions 1–7; goal 4. -- `links[source][target]` has −1/0/+1 entries and zero diagonal. -- Direct links apply once; no cascading; the selected plate's effect is implicit. -- A legal command keeps every affected pin in range throughout the movement. -- Module commands are zero-based `[index, delta]`, nonzero delta −6…+6. -- `solveLock(state, links)` has exactly two inputs; no default export. -- Successful solutions minimize grouped actions. Distinct controls and individual - shifts are separate reported metrics, not additional global guarantees. -- `null` means proved unreachable. Resource exhaustion throws `SearchLimitError`. -- Inputs are not mutated. Runtime validation and exact integer/rational arithmetic - cannot be replaced by assertions, coercions or floating-point tolerances. - -## Architecture - -- `src/index.ts`: public facade and explicit type/error exports. -- `src/lock-model.ts`: validated immutable input; single owner of input invariants. -- `src/matrix-analysis.ts`: exact rational Gauss–Jordan analysis. -- `src/matrix-search.ts`: certificate and exact A*. -- `src/search-bfs.ts`: exact singular-case fallback. -- `src/search-limits.ts`: prepared effects, encoding and computation budgets. -- `src/indexed-heap.ts`: priority queue; `src/indexed.ts`: checked indexed access. -- `cli/`: Node-only adapter preserving historical console/JSON conventions. -- `benchmarks/`: pinned fixtures, immutable references, independent replay and reports. -- `scripts/`: Vite build, consumer verification, benchmarks and release orchestration. -- `tests/`: behavior, exhaustive oracle, static type, distribution and real browser checks. - -Use composition and small domain objects with clear invariants. Keep exact -arithmetic as functions where a class adds no state or responsibility. Avoid -frameworks, duplicate validation policies, abstraction registries and public -options without a demonstrated requirement. Apply SOLID, KISS, YAGNI and DRY -proportionately. Test observable behavior, not private implementation inventories. - -## Changes and verification - -Start behavioral changes with a failing test and record the relevant red/green -evidence. Use pnpm and the exact current dependencies in the lockfile. Keep strict -TypeScript checks and lint warnings-as-errors. No `any`, `@ts-ignore`, non-null -assertions or unjustified type assertions; `@ts-expect-error` is restricted to -negative type-contract tests. Use current official tool documentation when an API -changes. Do not disable library checking to hide dependency declaration defects. - -Local gates precede workflow edits: - -```sh -pnpm install --frozen-lockfile -pnpm test -pnpm test:coverage -pnpm benchmark --reference ../unlockmyloot --output-dir artifacts/benchmark -``` - -Core and shipped CLI coverage must be at least 80% for every coverage metric. -Every one of the 45 locks must replay legally and retain minimum action counts. -The independent small-state oracle and all required real browser projects must -pass. Verify the built files, isolated CLI and clean installed tarball; source-only -or VM tests do not establish browser/distribution compatibility. - -Compare performance against the PR target in the same environment, preserving -raw samples and noise calibration. Never lower expected action counts or update -snapshots merely to turn a regression green. An inconclusive timing result stays -inconclusive. Record exact revisions, artifact hashes and environment details. - -An independent reviewer checks the final implementation, tests, mathematical -claims and owner principles before the draft PR is declared ready for review. -See the [independent migration review](docs/reviews/2026-09-07-principles-review.md) -for the recorded checks and their limits. The reference branch remains unchanged. Do not merge main, publish packages or -modify the upstream project as part of this migration. - -## Repository hygiene - -Keep README and current engineering docs in English; preserve Russian CLI output. -Use the same focused docs from README and this file instead of duplicating a wiki. -Never commit secrets, uploaded PDFs, `project_sources/`, `upload/`, dependency -folders or generated build/test artifacts. Do not edit `.env` files. Historical -benchmark reports and pinned reference sources are intentionally versioned. -Use official vendor Actions pinned to verified full SHAs, official npm/gh CLIs, -and standard free public runners. No separate CDN deployment service is needed. +Read [README](README.md), [API](wiki/api.md), [configuration](wiki/configuration.md), +[mathematics](wiki/algorithm.md) and relevant tests before editing. The approved +work is in the [current plan](docs/superpowers/plans/2026-09-07-simplify-library.md). + +## Contract and ownership + +- `solveLock(state, links, config?)` returns readonly `[index, delta]` commands. +- `createSolverConfig(overrides?)` validates settings and fills frozen defaults. +- Positions 1–7, target 4, at least two plates; source-first matrix, zero diagonal, + entries −1/0/+1, direct effects without cascading. Every affected pin stays in range. +- Commands have zero-based indices and nonzero signed displacement −6…+6. + Inputs and caller configuration remain unchanged. +- Successful paths minimize grouped actions. Do not promise minimum unit shifts, + distinct plates or universal timing. `null` is proved unreachable; resource + exhaustion throws `SearchLimitError`; invalid lock/config throws `LockInputError`. +- `src/index.ts` and `src/cli.ts` are the two build entries. CLI invokes the same + public solver/factory. Shared mathematics has one implementation. +- Keep source flat under `src/`; tests in `tests/unit`, `tests/e2e` and + `tests/benchmarks`; maintained documentation in `wiki/`. `.github/scripts` + contains necessary build/release coordination. Keep only the current plan in `docs/`. + +## Engineering + +Use TDD with observed failing behavior before changes. Tests use independent +replay and an exhaustive oracle; avoid assertions that mirror private structure. +Use domain terms and clear invariant ownership. Prefer composition, small stateful +classes and plain functions over registries, extra layers or helper-file scaffolding. +Apply SOLID, DRY, KISS and YAGNI proportionately. + +Use pnpm and exact current stable dependencies with a frozen lockfile. Preserve +strict TypeScript, checked indexed access, exact optional properties and +`skipLibCheck: false`. No `any`, non-null assertions, `ts-ignore` or unchecked casts. +Explained `ts-expect-error` belongs only in negative type tests. Do not patch +libraries or weaken checks to conceal incompatibilities; verify official APIs. + +## Verification + +Run focused checks and `pnpm check`, then `pnpm test`. Coverage includes all shipped +source and requires 80% per metric. Verify Node.js 22/24/26, real Chromium/Firefox/ +WebKit, all five generated files, Workers and an installed archive. + +Run the [45-input benchmark](wiki/benchmarks.md) without competing heavy jobs. +Use an independently checked-out actual PR target or verified own previous release. +Candidate results cannot supply their own baseline. Preserve raw calibration and +bounded repeats; uncertainty remains inconclusive. Never change expected minima +or thresholds merely to turn a regression green. + +All local gates precede workflow edits. Independently review the final diff and +check hosted CI on the exact head. Working logs/review outputs belong in ignored +`artifacts/` and CI artifacts. Use official SHA-pinned Actions, npm/gh CLIs and free +standard runners. Keep the PR draft; merging and publication are separate actions. + +## Hygiene + +Track TypeScript source, fixed test inputs, Wiki and configuration. Do not track +JavaScript/MJS/MTS sources, copied implementations, builds, result snapshots, +benchmark reports, coverage, traces, reviewer reports, dependencies, uploaded PDFs +or credentials. Do not edit `.env` files. Do not reference other solver projects +in current code, data, docs or the PR description. Official tool documentation +and this project's own package/repository/CDN URLs remain valid. diff --git a/README.md b/README.md index d789eca..db437c0 100644 --- a/README.md +++ b/README.md @@ -1,204 +1,82 @@ # Gothic Lock Solver -An exact **minimum-action** solver for the coupled plate locks in Gothic 1 Remake. -A small TypeScript library for browsers, Web Workers and Node.js, with a standalone -CLI. It has no runtime dependencies. +An exact minimum-action solver for Gothic 1 Remake coupled plate locks. A +TypeScript library and standalone Node.js CLI share one mathematical core with +no runtime dependencies. One action can move a plate several positions. -One action selects a plate and moves it several positions in one direction. -The solver minimizes these grouped actions, rather than individual clicks. -It combines exact matrix analysis, an optimality certificate, A* and a BFS fallback. -[The algorithm and its proof](docs/algorithm.md) explain the guarantee and its limits. +> The first public package is being prepared. npm/CDN examples become available +> after publication; replace `VERSION` with an exact published version. -> Publication status: this branch prepares the first public release. The package -> and CDN examples below become available after the first verified npm publication. -> Replace `VERSION` with an exact published version; these are integration templates. - -## Install and import +## Library ```sh npm install gothic-lock-solver ``` ```ts -import { solveLock } from "gothic-lock-solver"; - -const commands = solveLock([6, 2], [[0, -1], [0, 0]]); -// [[0, -2]]: decrease pin 0 by two; its reverse link increases pin 1 by two. -``` - -A namespace import is also supported: - -```ts -import * as GothicLockSolver from "gothic-lock-solver"; +import { solveLock, createSolverConfig } from "gothic-lock-solver"; -const commands = GothicLockSolver.solveLock([6, 2], [[0, -1], [0, 0]]); +const state = [6, 2] as const; +const links = [[0, -1], [0, 0]] as const; +solveLock(state, links); // [[0, -2]] +solveLock(state, links, createSolverConfig({ maxVisited: 500_000 })); ``` -There is no default export. Type declarations ship with the package. -[The complete contract](docs/api.md) covers tuple inference, validation and errors. +Namespace imports (`import * as GothicLockSolver`) are also supported. Positions +are 1–7, the goal is 4, and `links[source][target]` is −1/0/+1 with zero diagonal. +Links apply directly without cascading. Commands are `[zeroBasedIndex, signedPinDelta]`. +Inputs remain unchanged; successful results minimize grouped actions. -## Use directly in HTML +`[]` means already open; `null` means proved unreachable. Invalid input/config +throws `LockInputError`; exhausted computation throws `SearchLimitError`. +Read [API](wiki/api.md), [configuration](wiki/configuration.md) and +[mathematics and complexity](wiki/algorithm.md) for precise guarantees and limits. -No build step, package installation or CDN account is required by the integrator. -The npm package already contains the exact files served by jsDelivr. +## Browser and CLI ```html ``` -For browser modules: +Browser modules import named exports from the corresponding `.mjs` URL. -```html - +```sh +gothic-lock-solver lock.json --config solver.config.json --output solution.json +node gothic-lock-solver.cli.mjs lock.json --config solver.config.json ``` -Pin the full version and file path. The library is synchronous; use a -[Web Worker](docs/api.md#web-workers) for an interface that must remain responsive -while solving a difficult lock. - -## Contract at a glance +`lock.json` contains `state` and `links`; config JSON contains partial overrides. +Both flags are optional. CLI messages are Russian; exits are 0 solved, 2 unreachable +and 1 error. `--help` lists options. Use maintained Node.js 22, 24 or 26. -```ts -solveLock(state, links): readonly Command[] | null -// Command = readonly [index: number, delta: -6 | ... | -1 | 1 | ... | 6] -``` +Vite builds `src/index.ts` and `src/cli.ts` into five self-contained choices: -| Value | Meaning | -| --- | --- | -| `state[i]` | Pin position, an integer from 1 to 7; the goal is 4 | -| `state.length` | Number of plates, at least 2 | -| `links[source][target]` | Direct influence: −1 reverse, 0 none, +1 same direction | -| Diagonal | Always 0; the selected plate's own movement is implicit | -| `[index, delta]` | Zero-based plate index and signed change in numeric pin position | -| `[]` | Already open | -| `null` | The target was proved unreachable | -| `LockInputError` | Invalid values or dimensions | -| `SearchLimitError` | A computation limit was reached; this does not prove impossibility | - -Links do not cascade. Every affected pin must stay in positions 1–7 throughout -an action. Inputs are copied and never mutated. Positive delta means a higher -**pin position**; map it to physical left/right in the consuming UI. - -The model does not impose an eight-plate maximum. The current state encoding -supports up to 18 plates, with additional search budgets. Runtime and memory can -grow exponentially; successful results retain the exact minimum-action guarantee. -The solver does not promise minimum individual clicks or minimum distinct plates -among equally short action sequences. - -## Choose a distribution file - -| File in `dist/` | Use | +| Generated file | Consumer | | --- | --- | -| `gothic-lock-solver.mjs` | Readable ESM, Node.js or browser import | -| `gothic-lock-solver.min.mjs` | Minified ESM | -| `gothic-lock-solver.js` | Readable classic browser script, global `GothicLockSolver` | -| `gothic-lock-solver.min.js` | Minified classic browser script | -| `gothic-lock-solver.cli.mjs` | Standalone Node.js command-line tool | - -Each runtime file is self-contained. The CLI uses only built-in Node.js modules; -the four core files use no Node.js or DOM APIs, additional chunks or network calls. -The classic files are IIFEs, not CommonJS modules. Declarations, license and build -checksums accompany the runtime files. - -## Run the CLI +| `gothic-lock-solver.mjs` / `gothic-lock-solver.min.mjs` | Readable / minified ESM | +| `gothic-lock-solver.js` / `gothic-lock-solver.min.js` | Readable / minified classic script | +| `gothic-lock-solver.cli.mjs` | Standalone Node.js CLI | -Use a maintained Node.js 22, 24 or 26 release (latest patch recommended). After installing the package, run `gothic-lock-solver`. -Alternatively, download the CLI release asset and run it directly: - -```sh -node gothic-lock-solver.cli.mjs lock.json --output solution.json -``` - -```json -{ - "state": [6, 2], - "links": [[0, -1], [0, 0]] -} -``` +Core browser builds use ES2022. A Worker can isolate synchronous search from the UI. -The CLI retains the original Russian console output and JSON result format. -Its compatibility adapter uses **one-based plates**, `left` for increasing pin -position and `right` for decreasing it. The module API uses zero-based tuples. -Exit codes are `0` for solved, `2` for proved unreachable and `1` for input, -filesystem or resource errors. No output file is written unless `--output` is -supplied; an existing output file is replaced. See [CLI details](docs/api.md#cli). - -## Evidence and development - -The test corpus contains all **45 pinned catalog locks**, plus all 441 two-plate -configurations and 192 reproducible three-plate configurations. An independent -unit-step simulator checks every returned path; a separate exhaustive oracle -checks minimum actions on small models. - -Validation covers Node.js, real Chromium/Firefox/WebKit, both module bundles, -both classic bundles, Workers, the installed npm tarball and the standalone CLI. -Production core and CLI coverage must be at least **80% each for statements, -branches, functions and lines**. Type checking uses strict TypeScript 7; lint -warnings are errors. - -A [complete measured run](docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md) -compares all 45 locks on clean source -[`1651cd4`](https://github.com/Marcus-Rise/gothic-lock-solver/commit/1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9), -Node.js 24.19.0, Linux and AMD EPYC 9V74: - -| Implementation | Actions A | Distinct plates U | Unit shifts C | Sum of per-lock medians | -| --- | ---: | ---: | ---: | ---: | -| TypeScript library | 483 | 245 | 1,723 | 47.363 ms | -| Preserved matrix reference | 483 | 245 | 1,723 | 48.507 ms | -| Original BFS | 483 | 245 | 1,727 | 2,653.701 ms | -| Pinned Unlock My Loot | 491 | 245 | 1,695 | 1,517.403 ms | - -Against Unlock My Loot, five locks require fewer actions and forty tie, with -no action-count losses. It optimizes individual shifts first, so its smaller C -is a different trade-off. The TypeScript migration preserves A/U/C for every -lock compared with our matrix reference. - -Timing against the matrix reference is **inconclusive**: six per-lock comparisons -passed and thirty-nine remain uncertain. One bounded repeat detected a roughly -7 μs slowdown on `alberto-mine-hut-chest`; the conflicting attempts are preserved, -not dismissed. A smaller aggregate does not prove every lock became faster. -Whole-catalog process-memory comparison passed: median Linux VmHWM was -90.04 MiB versus 91.45 MiB. This includes Node and the harness, not just solver -allocations. The time column sums per-lock medians; it is not one measured -whole-catalog latency. - -The [machine-readable evidence](docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark-evidence.json) -links the complete raw samples, paths, environment and hashes. Seven timing -repetitions after two warmups, unchanged controls and bounded repeats make the -limits of these observations explicit. - -[Benchmark documentation](benchmarks/README.md) describes the full reports, -immutable fixtures, paired timing, peak RSS and baseline comparisons. -Actions, distinct selected plates and individual shifts are reported separately. -A timing result is `passed`, `regression` or `inconclusive`; noisy measurements -are not silently declared successful. Machine-specific timings are evidence for -that environment, not a universal latency promise. +## Development ```sh pnpm install --frozen-lockfile +pnpm exec playwright install --with-deps chromium firefox webkit pnpm test -pnpm test:coverage -pnpm benchmark --reference ../unlockmyloot --output-dir artifacts/benchmark ``` -Read [development and architecture](docs/development.md), -[algorithm and complexity](docs/algorithm.md), [release process](docs/releasing.md), -and [agent instructions](AGENTS.md) before contributing. Local verification -precedes workflow integration. The reference implementation remains available in -`feat/matrix-astar-benchmarks` and as a hash-pinned benchmark reference. +Tests cover 45 fixed inputs, independent small-state oracles, configuration, +Node/browser consumers and the installed package. Coverage requires 80% each for +statements, branches, functions and lines; strict types and lint warnings are enforced. -## Attribution and license +Read [development](wiki/development.md), [benchmarks](wiki/benchmarks.md), +[releases](wiki/releasing.md) and [AGENTS](AGENTS.md). Generated builds, snapshots, +reports and coverage are CI artifacts, with release evidence attached to GitHub +Releases. They are not committed to the source repository. -Apache-2.0; see [LICENSE](LICENSE). Catalog provenance and hashes are recorded in -[the fixture manifest](benchmarks/fixtures/manifest.json). -[Unlock My Loot](https://unlockmyloot.com/) and its -[source repository](https://github.com/1h8s/unlockmyloot) provide the catalog and -comparison implementation. Its solver is measured from a separately checked-out, -pinned upstream revision and is not bundled or republished in this package. +Apache-2.0 — see [LICENSE](LICENSE). diff --git a/benchmarks/README.md b/benchmarks/README.md deleted file mode 100644 index 9164ab4..0000000 --- a/benchmarks/README.md +++ /dev/null @@ -1,194 +0,0 @@ -# Reproducible benchmark evidence - -The 45 fixed catalog inputs in `fixtures/catalog.json` come from -[UnlockMyLoot at eee0bf50ebcb7fffd2b47954fd76bb015854e365](https://github.com/1h8s/unlockmyloot/tree/eee0bf50ebcb7fffd2b47954fd76bb015854e365). -The original fixture bytes, IDs, initial positions, directed links, provenance, -encoded lock codes, optimum grouped-action counts and historic paths are unchanged. -The catalog SHA256 is -`0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675`. -The loader verifies this immutable hash independently of the manifest. - -`baseline/` preserves the original Apache-2.0 BFS unchanged at -`a87e739a22ccc4215d6d9b14fa06b0738171cb60`. `reference-matrix/` preserves the -byte-identical Apache matrix implementation measured at -`6b1cfbc13bcec68f609d45d5dc97504dd84f1760`, including its original license. -Their manifests verify every preserved source file. These are benchmark references; -they are excluded from the published library. The external AGPL solver is never -vendored: `--reference` reads the separate pinned checkout, verifies its Git revision, -whole `index.html` hash and marked solver-block hash, then evaluates that unchanged -block in the same Node realm as the other timed implementations. - -## Running the controlled comparison - -```bash -pnpm build -pnpm benchmark --reference /path/to/unlockmyloot --output-dir docs/benchmarks - -pnpm benchmark:compare \ - --baseline-module /path/to/base/dist/gothic-lock-solver.mjs \ - --baseline-snapshot /path/to/base-quality.json \ - --baseline-format tuple \ - --reference /path/to/unlockmyloot \ - --output-dir docs/benchmarks -``` - -`benchmark` defaults to the pinned matrix reference. `benchmark:compare` requires -explicit module and snapshot paths from the PR target branch or previous release. -Both default to the candidate's built `dist/gothic-lock-solver.mjs`; the optional -`--candidate-module PATH` selects another named-export ESM. Its exact bytes and -SHA256 appear in evidence. `--baseline-format legacy` adapts the old one-argument -facade: one-based `plate` becomes a zero-based index, `left` becomes positive delta, -and `right` negative delta. `tuple` calls `solveLock(state, links)` directly. - -The default uses two warmups and seven measured repetitions per implementation -and lock. `--warmups N`, `--repetitions N` and `--memory-repetitions N` explicitly -set sampling. At least five matched measurements and five unchanged-control -measurements are needed for a performance verdict. Positive repetition counts -and nonnegative warmups are validated; duplicate and unknown arguments fail. - -`--smoke` selects the first two locks and labels the timing report `subset`. -`--skip-memory` explicitly records memory as unavailable and cannot establish a -performance pass. Neither is complete release evidence. Without `--reference`, -upstream cached counts remain visible, but there are no live upstream timings. - -## Quality and performance gates - -`validation.ts` independently replays tuple commands one unit division at a time. -It imports no production transition, state encoder, matrix analysis or search. -Every warmup and timed result must reach the goal, preserve input data and repeat -its deterministic path. Both candidate and baseline must attain the exact A -minimum originally proven by BFS on every fixture. Original BFS paths must match -the preserved path; live upstream paths and metrics must match pinned observations. - -Metrics are kept separately for each implementation and each lock: - -| Metric | Meaning | Comparison rule | -| --- | --- | --- | -| A | Grouped commands | Must equal the fixture's proven optimum | -| U | Distinct selected plates | An increase over the target snapshot fails quality | -| C | Sum of absolute selected-plate deltas | An increase over the target snapshot fails quality | -| plateSwitches | Changes of selected plate between adjacent commands | An increase fails this migration's quality gate | - -Only A is the solver's optimization objective. U/C/switches are descriptive; the -migration still must preserve them unless a separate reviewed change explicitly -accepts different behavior. Improving one metric does not conceal deterioration -in another. A bad path, lost solvability, input mutation, resource failure or -invalid command is a deterministic failure independent of timing uncertainty. - -The live baseline must reproduce the supplied target snapshot, including commands. -Candidate expectations are never consulted. Snapshot source module SHA256 must -match the actual loaded baseline. Thus editing the candidate's snapshot cannot -mask a regression against the PR target. Historic timing values are provenance -only and never establish a current performance pass. - -Timing calls share one Node process. The harness calibrates the unchanged baseline -against itself, then rotates measured implementation order by lock and round. -The public call is inside the timer; input cloning, normalization, independent -replay, metric calculation and reporting are outside it. Public validation and -legacy result construction are part of their respective public call costs. -Raw sample order, min, median, nearest-rank p95 and max are retained. - -The performance procedure uses paired log ratios and a conservative uncertainty -envelope (`mean ± 2.776 × standard error`, at least five observations). This is a -practical noise screen, not a claim of an exact distribution-free confidence -interval. The unchanged-control envelope determines the tolerance. A control -envelope wider than 25% cannot certify equivalence; it can still identify a -slowdown clearly exceeding that envelope. This ceiling rejects noisy evidence; -it is not a regression allowance. - -- `passed`: the candidate's upper slowdown envelope lies within the calibrated - control envelope. -- `regression`: a slowdown exceeds the envelope and is confirmed in one bounded - repeat. -- `inconclusive`: insufficient samples, wide control noise, overlap or conflicting - initial/repeat observations. This does not mean that regression is absent. - -A non-pass receives one bounded repeat; both attempts and raw samples remain in -JSON. An initial regression followed by a pass remains inconclusive. No repeated -runs continue until a preferred verdict appears. Every per-lock timing verdict -is visible; aggregate time never hides a slower lock. A confirmed regression or -quality regression returns a nonzero CLI status. Inconclusive evidence is retained -for explicit release review; release readiness requires acceptance of that exact -evidence, not merely passing unit tests. - -## Memory and environment limits - -Memory is measured separately in fresh Node child processes, never inside the -timing process. Each implementation has one isolated observation per lock. These -individual observations are explicitly inconclusive about regressions because one -sample cannot estimate environmental variation. A second measurement series runs -the entire catalog in fresh processes, rotating candidate, base and unchanged -controls for five repetitions by default; a non-pass gets one bounded repeat. -The whole-catalog comparison concerns peak RSS of that workload, not the sum of -per-lock RSS. A smoke run restricts both timing and memory to the same two selected fixtures. - -On Linux the metric is `/proc/self/status` **VmHWM × 1024 bytes**, the current -process image's RSS high-water. The raw Node `process.resourceUsage().maxRSS × 1024` -is also retained: controlled investigation showed that it could retain a heavy -launcher's pre-exec high-water, producing an artificial floor in otherwise fresh -workers. Each sample includes startup/post-import/current-image peaks, current RSS, -raw getrusage peak, and a flag when the startup getrusage peak exceeds VmHWM. - -The [Linux kernel documentation](https://docs.kernel.org/filesystems/proc.html) -defines VmHWM and warns that scalable RSS accounting is asynchronous and approximate. -The [Node API documentation](https://nodejs.org/api/process.html#processresourceusage) -specifies maxRSS in kibibytes. Neither metric is exact algorithm allocation: they -include runtime, modules, fixtures and measurement infrastructure. On other -platforms raw Node maxRSS is labeled as a fallback and the memory verdict remains -inconclusive until the platform's process-image semantics are verified. No values -are pooled across platforms. Browser peak memory remains explicitly unavailable; -Node RSS is not browser heap or Web Worker memory. - -Every run records Node/V8/OS/CPU, runtime arguments, candidate/base revisions, -dirty state, TypeScript and harness hashes, exact ESM SHA256, four library -bundle sizes/hashes plus the separate Node CLI bundle, fixture/source pins, compared snapshot SHA256, samples, -paths, metrics and per-lock differences. See the algorithm documentation for -worst-case time and space complexity: these 45 measured configurations are not a -random sample and cannot establish asymptotic complexity or universal speedups. - -An isolated memory correction can retain earlier timing samples without rewriting -them: - -```bash -node scripts/benchmark-memory.ts /path/to/prior/benchmark.json \ - --reference /path/to/unlockmyloot --output-dir docs/benchmarks -``` - -The correction checks that candidate bytes are unchanged, creates a separate RSS -supplement and adds an explicit superseded-memory notice next to the original -report. The original hashed JSON/Markdown/evidence files remain unchanged. - -## Reports and deliberate snapshot updates - -Each successful correctness run atomically publishes a new `run-*` directory: - -- `benchmark.json`: complete paths, raw samples, calibration, differences, - memory observations and provenance. -- `benchmark.md`: English review report, totals and per-lock tables. -- `benchmark-evidence.json`: versioned release evidence with report SHA256 links, - source/artifact provenance, verdicts and the candidate `qualitySnapshot`. - -Existing reports and snapshots are never overwritten automatically. To establish -quality from the actual PR target code, or propose a separate snapshot update: - -```bash -node scripts/benchmark-snapshot.ts \ - --source-module /path/to/base/src/index.mjs \ - --source-format legacy \ - --reason 'Initial migration baseline from the actual PR target revision' \ - --output /new/path/base-quality.json -``` - -The explicit generator verifies all 45 paths, their exact minima and determinism, -records the source revision/hash and reason, and creates the destination -exclusively. An existing destination causes an error. It does not establish a -performance pass. Review any proposed new snapshot together with a matched -comparison; do not regenerate expected quality from candidate code inside PR CI. -For a previous release, use its `qualitySnapshot` from verified evidence and the -exact previously published ESM bytes as the baseline. - -`pnpm benchmark:vitest` runs representative fixed inputs through Vitest 5's test -context `bench.compare()` for day-to-day investigation. This developer report is -not the release gate and writes no tracked snapshot. The controlled harness above -is authoritative for matched environment evidence. The current API is documented -in the [official Vitest benchmarking guide](https://vitest.dev/guide/benchmarking). diff --git a/benchmarks/baseline/LICENSE b/benchmarks/baseline/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/benchmarks/baseline/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/benchmarks/baseline/README.md b/benchmarks/baseline/README.md deleted file mode 100644 index 3d852d7..0000000 --- a/benchmarks/baseline/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Pinned Apache BFS baseline - -`src/` and `LICENSE` are byte-for-byte copies from -[Marcus-Rise/gothic-lock-solver](https://github.com/Marcus-Rise/gothic-lock-solver/tree/a87e739a22ccc4215d6d9b14fa06b0738171cb60), -commit `a87e739a22ccc4215d6d9b14fa06b0738171cb60`, under Apache License 2.0. -`manifest.json` records each original file's SHA-256. The benchmark checks these -hashes before measuring the baseline's public `solveLock` facade. The original -algorithm, validation, transition ordering, result construction, and relative -imports are unchanged. This snapshot intentionally preserves the old N <= 7 API. - -UnlockMyLoot solver code is not distributed here. Its optional comparison loads -the unchanged, hash-verified function from a separately supplied checkout. diff --git a/benchmarks/baseline/manifest.json b/benchmarks/baseline/manifest.json deleted file mode 100644 index 489be60..0000000 --- a/benchmarks/baseline/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "a87e739a22ccc4215d6d9b14fa06b0738171cb60", - "license": "Apache-2.0", - "files": { - "src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", - "src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", - "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", - "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", - "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" - }, - "notes": "Unmodified source snapshots; imports remain relative within this directory. No upstream UnlockMyLoot solver code is included." -} diff --git a/benchmarks/baseline/src/index.mjs b/benchmarks/baseline/src/index.mjs deleted file mode 100644 index 5cc1f05..0000000 --- a/benchmarks/baseline/src/index.mjs +++ /dev/null @@ -1,10 +0,0 @@ -import { validateLockDefinition } from './lock-definition.mjs'; -import { createSolveResult } from './result.mjs'; -import { findShortestCommands } from './solver.mjs'; - -export function solveLock(value) { - const definition = validateLockDefinition(value); - return createSolveResult(definition, findShortestCommands(definition)); -} - -export { applyCommand, generateTransitions } from './transition.mjs'; diff --git a/benchmarks/baseline/src/lock-definition.mjs b/benchmarks/baseline/src/lock-definition.mjs deleted file mode 100644 index 63a5719..0000000 --- a/benchmarks/baseline/src/lock-definition.mjs +++ /dev/null @@ -1,57 +0,0 @@ -export class LockInputError extends Error { - constructor(message, options) { - super(message, options); - this.name = 'LockInputError'; - } -} - -export function validateLockDefinition(value) { - if (value === null || typeof value !== 'object' || Array.isArray(value)) { - throw new LockInputError('Определение замка должно быть JSON-объектом.'); - } - - const { state, links } = value; - if (!Array.isArray(state) || state.length < 2 || state.length > 7) { - throw new LockInputError('state должен содержать от 2 до 7 пластин.'); - } - - state.forEach((position, index) => { - if (!Number.isInteger(position) || position < 1 || position > 7) { - throw new LockInputError(`state[${index}] должен быть целым числом от 1 до 7.`); - } - }); - - if (!Array.isArray(links) || links.length !== state.length) { - throw new LockInputError(`links должен быть матрицей ${state.length}x${state.length}.`); - } - - links.forEach((row, rowIndex) => { - if (!Array.isArray(row) || row.length !== state.length) { - throw new LockInputError(`links[${rowIndex}] должен содержать ${state.length} элементов.`); - } - row.forEach((coefficient, columnIndex) => { - if (![-1, 0, 1].includes(coefficient)) { - throw new LockInputError(`links[${rowIndex}][${columnIndex}] должен быть -1, 0 или 1.`); - } - if (rowIndex === columnIndex && coefficient !== 0) { - throw new LockInputError(`links[${rowIndex}][${columnIndex}] на диагонали должен быть 0.`); - } - }); - }); - - return { - state: [...state], - links: links.map((row) => [...row]), - }; -} - -export function parseLockDefinition(text) { - try { - return validateLockDefinition(JSON.parse(text)); - } catch (error) { - if (error instanceof LockInputError) { - throw error; - } - throw new LockInputError(`Некорректный JSON: ${error.message}`, { cause: error }); - } -} diff --git a/benchmarks/baseline/src/result.mjs b/benchmarks/baseline/src/result.mjs deleted file mode 100644 index 03fd343..0000000 --- a/benchmarks/baseline/src/result.mjs +++ /dev/null @@ -1,59 +0,0 @@ -import { createTargetState } from './state-codec.mjs'; -import { applyCommand } from './transition.mjs'; - -export function createSolveResult(definition, commands) { - const initialState = [...definition.state]; - const targetState = createTargetState(initialState.length); - - if (commands === null) { - return { status: 'unsolvable', initialState, targetState, commands: [] }; - } - - let finalState = [...initialState]; - for (const command of commands) { - const nextState = applyCommand(finalState, definition.links, command); - if (nextState === null) { - throw new Error('Решатель вернул заблокированную команду.'); - } - finalState = nextState; - } - - if (!finalState.every((position) => position === 4)) { - throw new Error('Решатель не привел замок к целевому состоянию.'); - } - - const plateSwitches = commands.reduce((count, command, index) => { - if (index === 0 || command.plate === commands[index - 1].plate) { - return count; - } - return count + 1; - }, 0); - - return { - status: 'solved', - initialState, - targetState, - commands: commands.map((command) => ({ ...command })), - finalState, - metrics: { - commands: commands.length, - divisions: commands.reduce((sum, command) => sum + command.steps, 0), - plateSwitches, - }, - }; -} - -export function formatConsoleResult(result) { - if (result.status === 'unsolvable') { - return 'Решение не найдено.'; - } - if (result.commands.length === 0) { - return 'Замок уже открыт.'; - } - - const directionNames = { left: 'влево', right: 'вправо' }; - const lines = result.commands.map((command, index) => ( - `${index + 1}. Пластина ${command.plate} - ${directionNames[command.direction]} x${command.steps}` - )); - return [`Найдено команд: ${result.commands.length}`, ...lines].join('\n'); -} diff --git a/benchmarks/baseline/src/solver.mjs b/benchmarks/baseline/src/solver.mjs deleted file mode 100644 index 50d30b5..0000000 --- a/benchmarks/baseline/src/solver.mjs +++ /dev/null @@ -1,80 +0,0 @@ -import { - createTargetState, - decodeState, - encodeState, - stateSpaceSize, -} from './state-codec.mjs'; -import { generateTransitions } from './transition.mjs'; - -const UNSEEN = -2; -const ROOT = -1; - -export function packCommand(command) { - const directionBit = command.direction === 'left' ? 0 : 1; - return (((command.plate - 1) * 2 + directionBit) * 6) + command.steps - 1; -} - -export function unpackCommand(packed) { - const steps = (packed % 6) + 1; - const plateDirection = Math.floor(packed / 6); - return { - plate: Math.floor(plateDirection / 2) + 1, - direction: plateDirection % 2 === 0 ? 'left' : 'right', - steps, - }; -} - -function reconstructCommands(startCode, targetCode, parents, actions) { - const commands = []; - let code = targetCode; - while (code !== startCode) { - commands.push(unpackCommand(actions[code])); - code = parents[code]; - } - commands.reverse(); - return commands; -} - -export function findShortestCommands({ state, links }) { - const plateCount = state.length; - const startCode = encodeState(state); - const targetCode = encodeState(createTargetState(plateCount)); - if (startCode === targetCode) { - return []; - } - - const size = stateSpaceSize(plateCount); - const parents = new Int32Array(size); - parents.fill(UNSEEN); - const actions = new Uint8Array(size); - const queue = new Int32Array(size); - - parents[startCode] = ROOT; - let head = 0; - let tail = 0; - queue[tail] = startCode; - tail += 1; - - while (head < tail) { - const currentCode = queue[head]; - head += 1; - const currentState = decodeState(currentCode, plateCount); - - for (const transition of generateTransitions(currentState, links)) { - const nextCode = encodeState(transition.state); - if (parents[nextCode] !== UNSEEN) { - continue; - } - - parents[nextCode] = currentCode; - actions[nextCode] = packCommand(transition.command); - if (nextCode === targetCode) { - return reconstructCommands(startCode, targetCode, parents, actions); - } - queue[tail] = nextCode; - tail += 1; - } - } - - return null; -} diff --git a/benchmarks/baseline/src/state-codec.mjs b/benchmarks/baseline/src/state-codec.mjs deleted file mode 100644 index ea0431c..0000000 --- a/benchmarks/baseline/src/state-codec.mjs +++ /dev/null @@ -1,25 +0,0 @@ -export function stateSpaceSize(plateCount) { - return 7 ** plateCount; -} - -export function encodeState(state) { - let code = 0; - for (const position of state) { - code = code * 7 + (position - 1); - } - return code; -} - -export function decodeState(code, plateCount) { - const state = new Array(plateCount); - let remainder = code; - for (let index = plateCount - 1; index >= 0; index -= 1) { - state[index] = (remainder % 7) + 1; - remainder = Math.floor(remainder / 7); - } - return state; -} - -export function createTargetState(plateCount) { - return Array.from({ length: plateCount }, () => 4); -} diff --git a/benchmarks/baseline/src/transition.mjs b/benchmarks/baseline/src/transition.mjs deleted file mode 100644 index 6886872..0000000 --- a/benchmarks/baseline/src/transition.mjs +++ /dev/null @@ -1,46 +0,0 @@ -const DIRECTIONS = ['left', 'right']; - -export function applyCommand(state, links, command) { - const sourceIndex = command.plate - 1; - const directionDelta = command.direction === 'left' ? 1 : -1; - const nextState = [...state]; - - for (let targetIndex = 0; targetIndex < state.length; targetIndex += 1) { - const coefficient = targetIndex === sourceIndex - ? 1 - : links[sourceIndex][targetIndex]; - - if (coefficient === 0) { - continue; - } - - const nextPosition = state[targetIndex] - + directionDelta * coefficient * command.steps; - if (nextPosition < 1 || nextPosition > 7) { - return null; - } - nextState[targetIndex] = nextPosition; - } - - return nextState; -} - -export function generateTransitions(state, links) { - const transitions = []; - for (let plate = 1; plate <= state.length; plate += 1) { - const sourcePosition = state[plate - 1]; - for (const direction of DIRECTIONS) { - const sourceLimit = direction === 'left' - ? 7 - sourcePosition - : sourcePosition - 1; - for (let steps = sourceLimit; steps >= 1; steps -= 1) { - const command = { plate, direction, steps }; - const nextState = applyCommand(state, links, command); - if (nextState !== null) { - transitions.push({ command, state: nextState }); - } - } - } - } - return transitions; -} diff --git a/benchmarks/catalog.ts b/benchmarks/catalog.ts deleted file mode 100644 index 4b61a1d..0000000 --- a/benchmarks/catalog.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { createHash } from 'node:crypto'; -import { execFileSync } from 'node:child_process'; -import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { array, integer, legacyCommands, record, string, type Definition, type Metrics, type TupleCommand } from './validation.ts'; - -export const catalogSha256 = '0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675'; -export const referenceRevision = 'eee0bf50ebcb7fffd2b47954fd76bb015854e365'; -export type Fixture = { id: string; title: string; definition: Definition; expectedActions: number; - baselineCommands: readonly TupleCommand[]; historicalReference: Metrics & { commands: readonly TupleCommand[] } }; -export type SourcePin = { revision: string; solver: { path: string; sha256: string; blockSha256: string; startMarker: string; endMarker: string } }; -export function sha256(value: string | Uint8Array): string { return createHash('sha256').update(value).digest('hex'); } -export function jsonFile(path: string | URL): unknown { const parsed: unknown = JSON.parse(readFileSync(path, 'utf8')); return parsed; } -export function loadManifest(): Record { return record(jsonFile(new URL('./fixtures/manifest.json', import.meta.url))); } -export function readMetrics(value: unknown): Metrics { - const item = record(value, 'metrics'); - return { A: integer(item['A']), U: integer(item['U']), C: integer(item['C']), plateSwitches: integer(item['plateSwitches']) }; -} -export function loadCatalog(): Fixture[] { - const bytes = readFileSync(new URL('./fixtures/catalog.json', import.meta.url)); - if (sha256(bytes) !== catalogSha256 || loadManifest()['catalogSha256'] !== catalogSha256) throw new Error('Catalog hash differs from the immutable 45-fixture pin.'); - const parsed: unknown = JSON.parse(bytes.toString('utf8')); - const locks = array(parsed, 'catalog').map((raw): Fixture => { - const lock = record(raw, 'fixture'); - const definition = record(lock['definition']); - const historical = record(lock['historicalReference']); - return { id: string(lock['id']), title: string(lock['titleEn'] ?? lock['title']), expectedActions: integer(lock['expectedActions']), - definition: { state: array(definition['state']).map((value) => integer(value)), links: array(definition['links']).map((row) => array(row).map((value) => integer(value))) }, - baselineCommands: legacyCommands(lock['baselineCommands']), historicalReference: { ...readMetrics(historical), commands: legacyCommands(historical['commands']) } }; - }); - if (locks.length !== 45 || new Set(locks.map((lock) => lock.id)).size !== 45) throw new Error('Expected 45 unique catalog locks.'); - return locks; -} -export function git(directory: string, args: readonly string[]): string { - return execFileSync('git', ['-C', directory, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); -} -export function verifySource(directory: string | URL): Record { - const root = typeof directory === 'string' ? directory : fileURLToPath(directory); - const manifest = record(jsonFile(join(root, 'manifest.json')), 'source manifest'); - for (const [path, expected] of Object.entries(record(manifest['files']))) { - if (path.startsWith('/') || path.split('/').includes('..')) throw new Error('Unsafe source manifest path.'); - if (sha256(readFileSync(join(root, path))) !== string(expected)) throw new Error(`Source hash mismatch: ${path}`); - } - return manifest; -} -export function upstreamPin(): SourcePin { - const source = record(loadManifest()['source']); const solver = record(source['solver']); - if (source['revision'] !== referenceRevision) throw new Error('Unexpected upstream reference revision pin.'); - return { revision: referenceRevision, solver: { path: string(solver['path']), sha256: string(solver['sha256']), blockSha256: string(solver['blockSha256']), - startMarker: string(solver['startMarker']), endMarker: string(solver['endMarker']) } }; -} -export function verifyReferenceSource(input: { revision: string; html: string }, pin: SourcePin = upstreamPin()): string { - if (input.revision !== pin.revision) throw new Error(`Reference revision mismatch: ${input.revision}`); - if (sha256(input.html) !== pin.solver.sha256) throw new Error('Reference file hash mismatch.'); - const start = input.html.indexOf(pin.solver.startMarker); const end = input.html.indexOf(pin.solver.endMarker, start); - if (start < 0 || end < start) throw new Error('Reference block markers are missing.'); - const block = input.html.slice(start, end + pin.solver.endMarker.length); - if (sha256(block) !== pin.solver.blockSha256) throw new Error('Reference block hash mismatch.'); - return block; -} diff --git a/benchmarks/cli.ts b/benchmarks/cli.ts deleted file mode 100644 index 4f7dbe8..0000000 --- a/benchmarks/cli.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { readFileSync, readdirSync, statSync } from 'node:fs'; -import os from 'node:os'; -import { dirname, join, relative, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { catalogSha256, git, loadCatalog, sha256, upstreamPin, verifySource } from './catalog.ts'; -import { loadImplementation, loadUpstream, publishReport, readSnapshot, runComparison, type Implementation, type ModuleFormat } from './harness.ts'; -import { collectMemory, type MemorySpec } from './memory.ts'; -import { combineVerdicts } from './comparison.ts'; -const root = fileURLToPath(new URL('../', import.meta.url)); -export type BenchmarkOptions = { candidateModule: string; baselineModule: string; baselineSnapshot: string; - baselineFormat: ModuleFormat; reference?: string; outputDir: string; warmups: number; repetitions: number; - memoryRepetitions: number; smoke: boolean; skipMemory: boolean; help: boolean }; -export function parseBenchmarkArgs(args: readonly string[], compare = false): BenchmarkOptions { - const options: BenchmarkOptions = { candidateModule: join(root, 'dist/gothic-lock-solver.mjs'), - baselineModule: join(root, 'benchmarks/reference-matrix/src/index.mjs'), - baselineSnapshot: join(root, 'benchmarks/snapshots/reference-matrix.json'), baselineFormat: compare ? 'tuple' : 'legacy', - outputDir: join(root, 'docs/benchmarks'), warmups: 2, repetitions: 7, memoryRepetitions: 5, smoke: false, skipMemory: false, help: false }; - const seen = new Set(); - for (let index = 0; index < args.length; index += 1) { - const argument = args[index]; if (argument === undefined) throw new Error('Missing argument.'); - if (seen.has(argument)) throw new Error(`Duplicate argument: ${argument}`); seen.add(argument); - if (argument === '--smoke') { options.smoke = true; continue; } - if (argument === '--skip-memory') { options.skipMemory = true; continue; } - if (argument === '--help') { options.help = true; continue; } - const value = args[++index]; if (value === undefined || value === '' || value.startsWith('--')) throw new Error(`Missing argument value: ${argument}`); - switch (argument) { - case '--candidate-module': options.candidateModule = resolve(value); break; - case '--baseline-module': options.baselineModule = resolve(value); break; - case '--baseline-snapshot': options.baselineSnapshot = resolve(value); break; - case '--baseline-format': if (value !== 'tuple' && value !== 'legacy') throw new Error('Baseline format must be tuple or legacy.'); options.baselineFormat = value; break; - case '--reference': options.reference = resolve(value); break; - case '--output-dir': options.outputDir = resolve(value); break; - case '--warmups': case '--repetitions': case '--memory-repetitions': { - if (!/^(0|[1-9][0-9]*)$/.test(value)) throw new Error('Expected a nonnegative integer.'); - const count = Number(value); const minimum = argument === '--warmups' ? 0 : 1; - if (!Number.isSafeInteger(count) || count < minimum) throw new Error('Invalid repetition/warmup integer.'); - if (argument === '--warmups') options.warmups = count; - else if (argument === '--repetitions') options.repetitions = count; - else options.memoryRepetitions = count; - break; - } - default: throw new Error(`Unknown argument: ${argument}`); - } - } - if (compare && !options.help && (!seen.has('--baseline-module') || !seen.has('--baseline-snapshot'))) throw new Error('Comparison requires explicit --baseline-module and --baseline-snapshot from the target branch or previous release.'); - return options; -} -function hashFiles(directory: string, matches: (path: string) => boolean): Record { - const result: Record = {}; - const visit = (path: string): void => { - for (const entry of readdirSync(path, { withFileTypes: true })) { - const full = join(path, entry.name); - if (entry.isDirectory()) visit(full); - else if (entry.isFile() && matches(full)) result[relative(directory, full)] = sha256(readFileSync(full)); - } - }; - visit(directory); return Object.fromEntries(Object.entries(result).sort(([a], [b]) => a.localeCompare(b))); -} -function revision(directory: string): string | null { try { return git(directory, ['rev-parse', 'HEAD']); } catch { return null; } } -export function collectMetadata(options: BenchmarkOptions) { - const baselineSnapshot = readSnapshot(options.baselineSnapshot); - const moduleHash = sha256(readFileSync(options.baselineModule)); - if (baselineSnapshot.source['moduleSha256'] !== moduleHash) throw new Error('Baseline module hash differs from supplied snapshot source.'); - const runtimeFiles = ['gothic-lock-solver.mjs', 'gothic-lock-solver.min.mjs', 'gothic-lock-solver.js', 'gothic-lock-solver.min.js', 'gothic-lock-solver.cli.mjs']; - const artifacts = Object.fromEntries(runtimeFiles.map((name) => { - const path = join(root, 'dist', name); return [name, { bytes: statSync(path).size, sha256: sha256(readFileSync(path)) }]; - })); - return { environment: { node: process.version, v8: process.versions.v8, platform: process.platform, arch: process.arch, - cpu: os.cpus()[0]?.model ?? 'unknown', logicalCpus: os.cpus().length, osRelease: os.release(), totalMemoryBytes: os.totalmem(), execArgv: process.execArgv }, - source: { revision: revision(root), dirty: git(root, ['status', '--porcelain', '--untracked-files=normal']).length > 0, - files: hashFiles(join(root, 'src'), (path) => path.endsWith('.ts')), harness: hashFiles(join(root, 'benchmarks'), (path) => path.endsWith('.ts')), - scripts: hashFiles(join(root, 'scripts'), (path) => /benchmark[^/]*\.ts$/.test(path)) }, - candidate: { revision: revision(dirname(options.candidateModule)), moduleSha256: sha256(readFileSync(options.candidateModule)), format: 'tuple', module: options.candidateModule }, - baseline: { ...baselineSnapshot.source, module: options.baselineModule, moduleTree: hashFiles(dirname(options.baselineModule), (path) => path.endsWith('.mjs') || path.endsWith('.ts')), snapshotSha256: sha256(readFileSync(options.baselineSnapshot)), snapshot: options.baselineSnapshot, - kind: options.baselineModule.includes('reference-matrix') ? 'reference-baseline' : 'target-branch-or-previous-release' }, - fixtures: { count: 45, catalogSha256, manifestSha256: sha256(readFileSync(new URL('./fixtures/manifest.json', import.meta.url))) }, - originalBfs: verifySource(new URL('./baseline/', import.meta.url)), matrixReference: verifySource(new URL('./reference-matrix/', import.meta.url)), - upstream: { ...upstreamPin(), measured: options.reference !== undefined, externalCheckout: options.reference ?? null, - license: 'External AGPL source is hash-verified and executed only from the supplied pinned checkout; no upstream solver code is vendored.' }, artifacts }; -} -export async function main(compare: boolean): Promise { - const options = parseBenchmarkArgs(process.argv.slice(2), compare); - if (options.help) { - console.log('Usage: pnpm benchmark [--reference PATH] [--output-dir PATH] [--warmups 2] [--repetitions 7] [--memory-repetitions 5] [--smoke] [--skip-memory]'); - console.log('Compare additionally requires --baseline-module PATH --baseline-snapshot PATH [--baseline-format tuple|legacy]. Candidate defaults to the built ESM; --candidate-module PATH overrides it.'); - return; - } - const metadata = collectMetadata(options); - const candidate = await loadImplementation(options.candidateModule, 'tuple', 'Candidate TypeScript library'); - const baseline = await loadImplementation(options.baselineModule, options.baselineFormat, 'Target/reference baseline'); - const bfsModule = join(root, 'benchmarks/baseline/src/index.mjs'); - const bfs = await loadImplementation(bfsModule, 'legacy', 'Original Apache BFS'); bfs.expectedPath = 'bfs'; - const others: Record = { originalBfs: bfs }; - const memorySpecs: MemorySpec[] = [ { name: 'candidate', module: options.candidateModule, format: 'tuple' }, - { name: 'baseline', module: options.baselineModule, format: options.baselineFormat }, { name: 'originalBfs', module: bfsModule, format: 'legacy' } ]; - if (options.reference !== undefined) { others['upstream'] = loadUpstream(options.reference); memorySpecs.push({ name: 'upstream', module: options.reference, format: 'upstream' }); } - const catalog = loadCatalog(); const locks = options.smoke ? catalog.slice(0, 2) : catalog; - const report = runComparison({ locks, candidate, baseline, others, baselineSnapshot: readSnapshot(options.baselineSnapshot), repetitions: options.repetitions, warmups: options.warmups, - onProgress: (completed, total, id) => process.stderr.write(`[${completed}/${total}] ${id}: replay and optimum checked\n`) }); - const memory = options.skipMemory ? { performance: { verdict: 'inconclusive' as const }, available: false, reason: 'Memory explicitly skipped; no memory pass is established.', browser: { available: false } } - : collectMemory(locks, memorySpecs, options.memoryRepetitions, (line) => process.stderr.write(`${line}\n`)); - report.performance = combineVerdicts([report.performance, memory.performance.verdict]); - const paths = await publishReport(report, options.outputDir, metadata, memory); - console.log(JSON.stringify({ ...paths, lockCount: locks.length, quality: report.quality, performance: report.performance }, null, 2)); - // An uncertain result is retained as evidence for explicit review; deterministic failures and confirmed performance regressions fail CI. - if (report.quality === 'regression' || report.performance === 'regression') process.exitCode = 1; -} diff --git a/benchmarks/fixtures/catalog.json b/benchmarks/fixtures/catalog.json deleted file mode 100644 index 532d932..0000000 --- a/benchmarks/fixtures/catalog.json +++ /dev/null @@ -1,9390 +0,0 @@ -[ - { - "id": "ruined-tower-door", - "title": "Дверь Разрушенной башни", - "titleEn": "Ruined Tower door", - "url": "https://unlockmyloot.com/locks/ruined-tower-door/", - "code": "aUkAACUBAAJikA", - "flip": false, - "description": "Дверь у основания Разрушенной башни рядом со Старым лагерем", - "catalogMetadata": "6 плиток · 57 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 452, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/ruined-tower-door/index.html", - "en/locks/ruined-tower-door/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 11, - "research_section": "### 1. Дверь Разрушенной башни (Старый Лагерь)", - "research_provenance": [ - "- Источник: корректная комбинация от Лёши (июнь 2026), заменила прежний код", - "- СТАТУС: в каталоге. Это и есть тот замок, на котором ловили баг BFS-помощника." - ] - }, - "definition": { - "state": [ - 5, - 6, - 2, - 2, - 1, - 1 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - -1, - 1, - 1, - 0 - ], - [ - 0, - 0, - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - -1, - 1, - -1, - 0, - 0, - -1 - ], - [ - -1, - 1, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 17, - "baselineCommands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 91.817025, - "median": 96.49636299999997, - "max": 126.86948000000007 - } - } - }, - { - "id": "scatty-chest", - "title": "Сундук Скатти", - "titleEn": "Scatty's chest", - "url": "https://unlockmyloot.com/locks/scatty-chest/", - "code": "SojAgCBDQA", - "flip": false, - "description": "Сундук Скатти с кольцом для Фингерса - квест «Кольцо для Фингерса»", - "catalogMetadata": "5 плиток · 31 ход · 6 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 458, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/scatty-chest/index.html", - "en/locks/scatty-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 25, - "research_section": "### 2. Сундук Скатти (квест Фингерса «Кольцо для Фингерса»)", - "research_provenance": [ - "- СТАТУС: в каталоге (2-я карточка)." - ] - }, - "definition": { - "state": [ - 6, - 3, - 2, - 1, - 7 - ], - "links": [ - [ - 0, - 0, - 0, - 1, - 0 - ], - [ - 0, - 0, - 0, - 0, - 1 - ], - [ - 0, - 0, - 0, - 0, - -1 - ], - [ - 0, - 1, - -1, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 9, - "baselineCommands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 9.513588000000027, - "median": 9.766299000000117, - "max": 12.794235999999955 - } - } - }, - { - "id": "cor-galom-chest", - "title": "Сундук Кор Галома", - "titleEn": "Cor Galom's chest", - "url": "https://unlockmyloot.com/locks/cor-galom-chest/", - "code": "LagFFgA", - "flip": false, - "description": "Рецепт для квеста Декстера - между лабораторными столами на первом этаже", - "catalogMetadata": "4 плитки · 15 ходов · 5 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 464, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/cor-galom-chest/index.html", - "en/locks/cor-galom-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 37, - "research_section": "### 3. Сундук Кор Галома (рецепт для квеста Декстера)", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге (3-я карточка)." - ] - }, - "definition": { - "state": [ - 7, - 7, - 6, - 1 - ], - "links": [ - [ - 0, - 0, - 0, - 1 - ], - [ - 1, - 0, - 0, - 1 - ], - [ - 1, - -1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 5, - "baselineCommands": [ - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 5, - "U": 4, - "C": 15, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - } - ], - "historicalTimingMs": { - "min": 0.3375069999999596, - "median": 0.507082999999966, - "max": 0.6312800000000607 - } - } - }, - { - "id": "cor-galom-bedroom-chest", - "title": "Сундук в спальне Кор Галома", - "titleEn": "Cor Galom's bedroom chest", - "url": "https://unlockmyloot.com/locks/cor-galom-bedroom-chest/", - "code": "a2YMQAAIiECEAA", - "flip": false, - "description": "Спальня на втором этаже - кольцо очищения, свиток пиромантии и алхимический лут", - "catalogMetadata": "6 плиток · 60 ходов · 7 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 470, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/cor-galom-bedroom-chest/index.html", - "en/locks/cor-galom-bedroom-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 49, - "research_section": "### 3a. Сундук в спальне Кор Галома", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге (4-я карточка)." - ] - }, - "definition": { - "state": [ - 6, - 6, - 5, - 7, - 1, - 4 - ], - "links": [ - [ - 0, - 0, - 1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - -1, - 0, - 0, - -1, - 0 - ], - [ - -1, - 0, - 1, - 0, - 0, - 0 - ], - [ - 0, - -1, - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 14, - "baselineCommands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 30.516717999999855, - "median": 31.42274099999986, - "max": 34.788095999999996 - } - } - }, - { - "id": "swamp-camp-forge-chest", - "title": "Сундук в кузнице Болотного лагеря", - "titleEn": "Swamp Camp forge chest", - "url": "https://unlockmyloot.com/locks/swamp-camp-forge-chest/", - "code": "YQY0gBgAAGACIE", - "flip": false, - "description": "Кузница Болотного лагеря - одноручный топор на 29 урона и материалы", - "catalogMetadata": "6 плиток · 41 ход · 8 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 476, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/swamp-camp-forge-chest/index.html", - "en/locks/swamp-camp-forge-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 62, - "research_section": "### 3b. Сундук в кузнице Болотного лагеря", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 1, - 5, - 1, - 7, - 2, - 6 - ], - "links": [ - [ - 0, - 0, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - -1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0, - -1, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - -1 - ], - [ - 0, - -1, - 0, - 0, - 1, - 0 - ] - ] - }, - "expectedActions": 11, - "baselineCommands": [ - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 36.47057800000039, - "median": 37.77093500000001, - "max": 38.26945599999999 - } - } - }, - { - "id": "rice-lord-storeroom", - "title": "Склад Рисового лорда (3-й этаж)", - "titleEn": "Rice Lord's storeroom (3rd floor)", - "url": "https://unlockmyloot.com/locks/rice-lord-storeroom/", - "code": "RY2VUBQAiA", - "flip": false, - "description": "Дверь на 3-м этаже над рисовыми полями - на 2-м другой замок", - "catalogMetadata": "5 плиток · 30 ходов · 9 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 482, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/rice-lord-storeroom/index.html", - "en/locks/rice-lord-storeroom/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 76, - "research_section": "### 4. Склад Рисового лорда (Новый лагерь, рисовые поля, 3-й этаж)", - "research_provenance": [ - "- Источник: конфигурация от Лёши (июнь 2026).", - "- СТАТУС: в каталоге (4-я карточка)." - ] - }, - "definition": { - "state": [ - 3, - 7, - 2, - 6, - 5 - ], - "links": [ - [ - 0, - -1, - -1, - -1, - -1 - ], - [ - -1, - 0, - 0, - 0, - 0 - ], - [ - -1, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 9, - "baselineCommands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 4.488335000000006, - "median": 4.637408999999934, - "max": 4.859664000000066 - } - } - }, - { - "id": "gomez-chambers-door", - "title": "Дверь в покои Гомеза", - "titleEn": "Door to Gomez's chambers", - "url": "https://unlockmyloot.com/locks/gomez-chambers-door/", - "code": "YiNAIJpEEAiAAA", - "flip": false, - "description": "Дверь в покои Гомеза в замке Старого лагеря", - "catalogMetadata": "6 плиток · 28 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 488, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/gomez-chambers-door/index.html", - "en/locks/gomez-chambers-door/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 88, - "research_section": "### 5. Дверь в покои Гомеза (Старый Лагерь)", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 2, - 1, - 5, - 4, - 3, - 1 - ], - "links": [ - [ - 0, - 0, - 0, - -1, - 0, - 0 - ], - [ - -1, - 0, - 1, - -1, - -1, - 1 - ], - [ - 0, - 1, - 0, - 0, - 0, - 1 - ], - [ - 0, - 0, - 0, - 0, - 0, - -1 - ], - [ - 0, - -1, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 9, - "baselineCommands": [ - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 29.93992900000012, - "median": 30.68829599999981, - "max": 31.623243999999886 - } - } - }, - { - "id": "gomez-throne-hall-chest", - "title": "Сундук в тронном зале у Гомеза", - "titleEn": "Chest in Gomez's throne hall", - "url": "https://unlockmyloot.com/locks/gomez-throne-hall-chest/", - "code": "IIAKWgA", - "flip": false, - "description": "Справа от Гомеза - чертёж королевского оружия, кража не считается", - "catalogMetadata": "4 плитки · 16 ходов · 6 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 494, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/gomez-throne-hall-chest/index.html", - "en/locks/gomez-throne-hall-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 229, - "research_section": "### 5s. Сундук в тронном зале у Гомеза", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 1, - 3, - 1, - 1 - ], - "links": [ - [ - 0, - 0, - 0, - -1 - ], - [ - -1, - 0, - 1, - 1 - ], - [ - -1, - -1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 5, - "baselineCommands": [ - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 0.25226000000020576, - "median": 0.3677139999999781, - "max": 0.39675699999952485 - } - } - }, - { - "id": "old-camp-dungeon-door", - "title": "Дверь в темнице с расстрелянным призраком", - "titleEn": "Dungeon door by the executed ghost", - "url": "https://unlockmyloot.com/locks/old-camp-dungeon-door/", - "code": "YMTCAKIgIEBiAA", - "flip": false, - "description": "Вход в темницу под замком - расстрелянный призрак, квест Арлин", - "catalogMetadata": "6 плиток · 68 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 500, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/old-camp-dungeon-door/index.html", - "en/locks/old-camp-dungeon-door/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 240, - "research_section": "### 5t. Дверь в темнице с расстрелянным призраком", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 1, - 4, - 1, - 5, - 7, - 1 - ], - "links": [ - [ - 0, - -1, - 0, - 0, - 0, - 0 - ], - [ - -1, - 0, - -1, - 0, - -1, - 0 - ], - [ - -1, - 0, - 0, - 0, - 0, - -1 - ], - [ - 0, - 0, - 1, - 0, - 0, - 0 - ], - [ - 0, - 1, - -1, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 17, - "baselineCommands": [ - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 18, - "U": 6, - "C": 68, - "plateSwitches": 17, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 41.57797500000015, - "median": 42.28448800000024, - "max": 46.73487599999953 - } - } - }, - { - "id": "arlin-dungeon-key", - "title": "Ключ от темницы Арлина", - "titleEn": "Arlin's dungeon key", - "url": "https://unlockmyloot.com/locks/arlin-dungeon-key/", - "code": "gBDXAECQhAAQAQAIRAA", - "flip": false, - "description": "Камера Арлина в темнице - ключ у стражника или взлом", - "catalogMetadata": "7 плиток · 25 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 506, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/arlin-dungeon-key/index.html", - "en/locks/arlin-dungeon-key/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 250, - "research_section": "### 5u. Ключ от темницы Арлина", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 1, - 1, - 3, - 1, - 7, - 6, - 7 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 0, - -1, - 0 - ], - [ - 0, - 0, - 1, - 0, - -1, - 0, - 1 - ], - [ - 0, - 0, - 0, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - -1, - 0, - 0 - ], - [ - 0, - 0, - 0, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 1, - 0, - 0, - 0 - ], - [ - -1, - 0, - -1, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 7, - "baselineCommands": [ - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 7, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 7, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 22.995531999999912, - "median": 23.810668999999507, - "max": 25.53016800000023 - } - } - }, - { - "id": "gomez-wine-cellar", - "title": "Винный погреб Гомеза", - "titleEn": "Gomez's wine cellar", - "url": "https://unlockmyloot.com/locks/gomez-wine-cellar/", - "code": "bI02qgAAIBgEAg", - "flip": false, - "description": "Склад под Гомезом - дверь внизу вертикальной лестницы", - "catalogMetadata": "6 плиток · 31 ход · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 512, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/gomez-wine-cellar/index.html", - "en/locks/gomez-wine-cellar/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 260, - "research_section": "### 5v. Винный погреб Гомеза", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 7, - 3, - 2, - 6, - 2, - 6 - ], - "links": [ - [ - 0, - -1, - -1, - -1, - -1, - -1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0, - 1, - -1 - ], - [ - 0, - 0, - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 0, - -1, - 0, - 0 - ] - ] - }, - "expectedActions": 9, - "baselineCommands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 31, - "plateSwitches": 10, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 27.862570000000233, - "median": 28.006227000000763, - "max": 28.23831599999994 - } - } - }, - { - "id": "gomez-bedside-chest", - "title": "Сундук в ногах Гомеза", - "titleEn": "Gomez bedside chest", - "url": "https://unlockmyloot.com/locks/gomez-bedside-chest/", - "code": "SgjQQDUgCI", - "flip": false, - "description": "Сундук у кровати в ногах Гомеза, Старый Лагерь", - "catalogMetadata": "5 плиток · 68 ходов · 9 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 518, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/gomez-bedside-chest/index.html", - "en/locks/gomez-bedside-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": "Влад · DTF", - "research_line": 95, - "research_section": "### 5a. Сундук в ногах Гомеза (Старый Лагерь)", - "research_provenance": [ - "- Нашёл и прислал: Влад, DTF (https://dtf.ru/id644393). На карточке кредит «нашёл: Влад · DTF».", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 6, - 1, - 2, - 1, - 7 - ], - "links": [ - [ - 0, - -1, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0, - 1 - ], - [ - -1, - -1, - 0, - -1, - 1 - ], - [ - 0, - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 1, - 0, - 0 - ] - ] - }, - "expectedActions": 23, - "baselineCommands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 7.661264000000301, - "median": 7.950699999999415, - "max": 8.508680999999342 - } - } - }, - { - "id": "gomez-wall-chest", - "title": "Сундук у стены в покоях Гомеза", - "titleEn": "Wall chest in Gomez's chambers", - "url": "https://unlockmyloot.com/locks/gomez-wall-chest/", - "code": "ZDAkKqACQECQAA", - "flip": false, - "description": "Сундук у стены - со стороны комнаты Ворона, рекорд каталога", - "catalogMetadata": "6 плиток · 75 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 525, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/gomez-wall-chest/index.html", - "en/locks/gomez-wall-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 104, - "research_section": "### 5b. Сундук у стены в покоях Гомеза (со стороны Ворона)", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 3, - 1, - 7, - 1, - 2, - 2 - ], - "links": [ - [ - 0, - 0, - 0, - -1, - -1, - -1 - ], - [ - -1, - 0, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - -1, - 1, - 0 - ], - [ - 0, - 0, - 1, - 0, - 0, - 0 - ], - [ - 0, - -1, - 1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 20, - "baselineCommands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 83.76281300000028, - "median": 85.88850400000047, - "max": 89.70766300000014 - } - } - }, - { - "id": "voran-bedside-chest", - "title": "Сундук у кровати Ворона", - "titleEn": "Voran's bedside chest", - "url": "https://unlockmyloot.com/locks/voran-bedside-chest/", - "code": "jV46BEAAIAAAJLASAAA", - "flip": false, - "description": "Первый замок на 7 плиток - ключ от башни Ворона", - "catalogMetadata": "7 плиток · 38 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 531, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/voran-bedside-chest/index.html", - "en/locks/voran-bedside-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 112, - "research_section": "### 5c. Сундук у кровати Ворона", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 7, - 6, - 4, - 7, - 2, - 7, - 5 - ], - "links": [ - [ - 0, - 0, - 0, - -1, - 0, - -1, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - -1, - 1, - 0, - 1, - -1 - ], - [ - 0, - 0, - 0, - -1, - 1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 9, - "baselineCommands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 7, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 7, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 172.0179500000013, - "median": 178.24784999999974, - "max": 182.09039500000017 - } - } - }, - { - "id": "arto-room-door", - "title": "Дверь в комнату Арто", - "titleEn": "Door to Arto's room", - "url": "https://unlockmyloot.com/locks/arto-room-door/", - "code": "TbYwABSyBC", - "flip": false, - "description": "Комната Арто слева в покоях Гомеза - напротив Ворона", - "catalogMetadata": "5 плиток · 31 ход · 9 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 537, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/arto-room-door/index.html", - "en/locks/arto-room-door/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 180, - "research_section": "### 5m. Дверь в комнату Арто", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 7, - 7, - 7, - 7, - 2 - ], - "links": [ - [ - 0, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0 - ], - [ - -1, - -1, - 0, - 1, - 1 - ], - [ - -1, - 1, - 0, - 0, - 0 - ], - [ - 0, - -1, - 0, - 1, - 0 - ] - ] - }, - "expectedActions": 7, - "baselineCommands": [ - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - } - ], - "historicalTimingMs": { - "min": 4.352972000000591, - "median": 4.386361999999281, - "max": 4.6916220000002795 - } - } - }, - { - "id": "arto-far-bed-chest", - "title": "Сундук у дальней кровати Арто", - "titleEn": "Arto's far bed chest", - "url": "https://unlockmyloot.com/locks/arto-far-bed-chest/", - "code": "ZpEUEAAAgASIAk", - "flip": false, - "description": "Дальняя кровать - кольцо защиты от оружия, экстракты", - "catalogMetadata": "6 плиток · 40 ходов · 7 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 543, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/arto-far-bed-chest/index.html", - "en/locks/arto-far-bed-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 188, - "research_section": "### 5n. Сундук у дальней кровати Арто", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 4, - 3, - 3, - 2, - 1, - 6 - ], - "links": [ - [ - 0, - 0, - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - -1, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 1 - ], - [ - 0, - -1, - 0, - -1, - 0, - 0 - ], - [ - 0, - 0, - 0, - -1, - 1, - 0 - ] - ] - }, - "expectedActions": 11, - "baselineCommands": [ - { - "plate": 2, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 13, - "U": 5, - "C": 40, - "plateSwitches": 12, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 25.627033000000665, - "median": 26.17454699999871, - "max": 26.764604999998483 - } - } - }, - { - "id": "arto-near-bed-chest", - "title": "Сундук у ближней кровати Арто", - "titleEn": "Arto's near bed chest", - "url": "https://unlockmyloot.com/locks/arto-near-bed-chest/", - "code": "Y2WYAEGAAIGEAY", - "flip": false, - "description": "Ближняя кровать - кольцо мощи, руда, 52 хода", - "catalogMetadata": "6 плиток · 52 хода · 9 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 549, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/arto-near-bed-chest/index.html", - "en/locks/arto-near-bed-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 196, - "research_section": "### 5o. Сундук у ближней кровати Арто", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 2, - 6, - 5, - 6, - 5, - 7 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0, - 1, - -1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - -1, - 0, - 0, - 0 - ], - [ - 1, - -1, - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 1, - -1, - 0 - ] - ] - }, - "expectedActions": 13, - "baselineCommands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 71.42761900000005, - "median": 72.47732999999971, - "max": 73.87815799999953 - } - } - }, - { - "id": "gomez-bedrooms-door", - "title": "Дверь на этаже спален покоев", - "titleEn": "Door on Gomez's bedrooms floor", - "url": "https://unlockmyloot.com/locks/gomez-bedrooms-door/", - "code": "YHayiAREIAIABE", - "flip": false, - "description": "Первая дверь на этаже спален - комната с двумя сундуками", - "catalogMetadata": "6 плиток · 45 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 555, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/gomez-bedrooms-door/index.html", - "en/locks/gomez-bedrooms-door/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 17, - "research_section": "### 1. Дверь Разрушенной башни (Старый Лагерь)", - "research_provenance": [ - "- Источник: корректная комбинация от Лёши (июнь 2026), заменила прежний код", - "- СТАТУС: в каталоге. Это и есть тот замок, на котором ловили баг BFS-помощника." - ] - }, - "definition": { - "state": [ - 1, - 2, - 7, - 7, - 6, - 5 - ], - "links": [ - [ - 0, - -1, - -1, - 0, - -1, - 0 - ], - [ - 0, - 0, - 0, - 1, - 0, - 1 - ], - [ - 0, - 1, - 0, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - -1, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0, - 1, - 0 - ] - ] - }, - "expectedActions": 14, - "baselineCommands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 14, - "U": 6, - "C": 45, - "plateSwitches": 13, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 36.83029600000009, - "median": 37.16563900000074, - "max": 42.04815899999994 - } - } - }, - { - "id": "gomez-bedrooms-far-chest", - "title": "Сундук у дальней кровати (этаж спален)", - "titleEn": "Far bed chest (bedrooms floor)", - "url": "https://unlockmyloot.com/locks/gomez-bedrooms-far-chest/", - "code": "RzbBFITEQQ", - "flip": false, - "description": "Дальняя кровать - кольцо рудной кожи, экстракты, алкоголь", - "catalogMetadata": "5 плиток · 39 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 561, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/gomez-bedrooms-far-chest/index.html", - "en/locks/gomez-bedrooms-far-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 213, - "research_section": "### 5q. Сундук у дальней кровати (этаж спален)", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 4, - 5, - 7, - 7, - 7 - ], - "links": [ - [ - 0, - 0, - 0, - -1, - 0 - ], - [ - -1, - 0, - -1, - 1, - 0 - ], - [ - 0, - -1, - 0, - 1, - -1 - ], - [ - 0, - -1, - 0, - 0, - -1 - ], - [ - 0, - 0, - -1, - 0, - 0 - ] - ] - }, - "expectedActions": 14, - "baselineCommands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 16, - "U": 5, - "C": 39, - "plateSwitches": 15, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 5.34081900000092, - "median": 5.4789160000000265, - "max": 5.777416000000812 - } - } - }, - { - "id": "gomez-bedrooms-near-chest", - "title": "Сундук у ближней кровати (этаж спален)", - "titleEn": "Near bed chest (bedrooms floor)", - "url": "https://unlockmyloot.com/locks/gomez-bedrooms-near-chest/", - "code": "ZVbYAEKEBgECAA", - "flip": false, - "description": "Ближняя кровать - кольцо великой жизненной силы, 56 ходов", - "catalogMetadata": "6 плиток · 56 ходов · 8 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 567, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/gomez-bedrooms-near-chest/index.html", - "en/locks/gomez-bedrooms-near-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 221, - "research_section": "### 5r. Сундук у ближней кровати (этаж спален)", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 3, - 6, - 3, - 7, - 7, - 7 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0, - -1, - -1 - ], - [ - 0, - 1, - 0, - 0, - 0, - 0 - ], - [ - 1, - -1, - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 12, - "baselineCommands": [ - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 40.47176899999977, - "median": 40.801266000000396, - "max": 43.21825400000125 - } - } - }, - { - "id": "raven-tower-left-chest", - "title": "Левый сундук в башне Ворона", - "titleEn": "Raven Tower left chest", - "url": "https://unlockmyloot.com/locks/raven-tower-left-chest/", - "code": "bVyMKQAAGQAAWA", - "flip": false, - "description": "1-й этаж башни, слева у входа - 2 из 9 сундуков в каталоге", - "catalogMetadata": "6 плиток · 35 ходов · 9 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 573, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/raven-tower-left-chest/index.html", - "en/locks/raven-tower-left-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 121, - "research_section": "### 5d. Левый сундук в башне Ворона (1-й этаж)", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 7, - 6, - 4, - 5, - 5, - 4 - ], - "links": [ - [ - 0, - 0, - 0, - -1, - -1, - 1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 1 - ], - [ - -1, - 1, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 1, - 1, - -1, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 11, - "baselineCommands": [ - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 45.528119999999035, - "median": 46.7260040000001, - "max": 47.06144800000038 - } - } - }, - { - "id": "raven-tower-right-chest", - "title": "Правый сундук в башне Ворона", - "titleEn": "Raven Tower right chest", - "url": "https://unlockmyloot.com/locks/raven-tower-right-chest/", - "code": "bEhYigAEVCQEAA", - "flip": false, - "description": "1-й этаж башни, справа у входа - 2 из 9 сундуков в каталоге", - "catalogMetadata": "6 плиток · 23 хода · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 579, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/raven-tower-right-chest/index.html", - "en/locks/raven-tower-right-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 130, - "research_section": "### 5e. Правый сундук в башне Ворона (1-й этаж)", - "research_provenance": [ - "- Источник: сверено в игре (июнь 2026).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 7, - 2, - 2, - 1, - 3, - 7 - ], - "links": [ - [ - 0, - 0, - -1, - 0, - -1, - -1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - 0, - 1, - 1 - ], - [ - 1, - 0, - 0, - 0, - -1, - 1 - ], - [ - 0, - 0, - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 8, - "baselineCommands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 15.836498000000574, - "median": 16.735591999999087, - "max": 18.030751000000237 - } - } - }, - { - "id": "raven-tower-floor2-stairs-chest", - "title": "Сундук у лестницы (2-й этаж башни)", - "titleEn": "Chest by stairs (Raven Tower floor 2)", - "url": "https://unlockmyloot.com/locks/raven-tower-floor2-stairs-chest/", - "code": "YqahUKAAACIIAA", - "flip": false, - "description": "2-й этаж - сразу после лестницы, вино и пиво", - "catalogMetadata": "6 плиток · 48 ходов · 8 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 585, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/raven-tower-floor2-stairs-chest/index.html", - "en/locks/raven-tower-floor2-stairs-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 137, - "research_section": "### 5f. Сундук у лестницы на 2-м этаже башни Ворона", - "research_provenance": [ - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 2, - 3, - 5, - 7, - 6, - 1 - ], - "links": [ - [ - 0, - 1, - 1, - 1, - 0, - 0 - ], - [ - -1, - 0, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - -1, - 0 - ], - [ - -1, - 0, - 0, - -1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 16, - "baselineCommands": [ - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 53.395453000001, - "median": 54.389939999999115, - "max": 56.43529800000033 - } - } - }, - { - "id": "raven-tower-floor2-chest-2", - "title": "Второй сундук на 2-м этаже башни", - "titleEn": "Second chest on Raven Tower floor 2", - "url": "https://unlockmyloot.com/locks/raven-tower-floor2-chest-2/", - "code": "Q6XCAhAglI", - "flip": false, - "description": "2-й этаж - 12 приборов и 8897 монет", - "catalogMetadata": "5 плиток · 27 ходов · 8 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 591, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/raven-tower-floor2-chest-2/index.html", - "en/locks/raven-tower-floor2-chest-2/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 143, - "research_section": "### 5g. Второй сундук на 2-м этаже башни Ворона", - "research_provenance": [ - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 2, - 7, - 5, - 6, - 7 - ], - "links": [ - [ - 0, - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0, - 0 - ], - [ - -1, - 0, - 0, - 0, - 1 - ], - [ - 0, - 0, - 1, - 0, - 0 - ], - [ - -1, - -1, - 1, - 0, - 0 - ] - ] - }, - "expectedActions": 8, - "baselineCommands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 27, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - } - ], - "historicalTimingMs": { - "min": 3.0696099999986473, - "median": 3.0996259999992617, - "max": 3.4110639999998966 - } - } - }, - { - "id": "raven-tower-floor3-entrance-chest", - "title": "Сундук у входа (3-й этаж башни)", - "titleEn": "Chest at entrance (Raven Tower floor 3)", - "url": "https://unlockmyloot.com/locks/raven-tower-floor3-entrance-chest/", - "code": "QmzSSoBAAg", - "flip": false, - "description": "3-й этаж - волчьи когти, зубы, приборы", - "catalogMetadata": "5 плиток · 44 хода · 8 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 597, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/raven-tower-floor3-entrance-chest/index.html", - "en/locks/raven-tower-floor3-entrance-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 148, - "research_section": "### 5h. Сундук у входа на 3-м этаже башни Ворона", - "research_provenance": [ - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 2, - 2, - 6, - 5, - 7 - ], - "links": [ - [ - 0, - -1, - 1, - 0, - -1 - ], - [ - 1, - 0, - 1, - 1, - 0 - ], - [ - 0, - 0, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 14, - "baselineCommands": [ - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 6.549548999999388, - "median": 6.864462999998068, - "max": 7.441822000000684 - } - } - }, - { - "id": "raven-tower-floor3-empty-chest", - "title": "Пустой сундук (3-й этаж башни)", - "titleEn": "Empty chest (Raven Tower floor 3)", - "url": "https://unlockmyloot.com/locks/raven-tower-floor3-empty-chest/", - "code": "RYIBEylAIA", - "flip": false, - "description": "3-й этаж - сундук пустой", - "catalogMetadata": "5 плиток · 37 ходов · 9 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 603, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/raven-tower-floor3-empty-chest/index.html", - "en/locks/raven-tower-floor3-empty-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 154, - "research_section": "### 5i. Пустой сундук на 3-м этаже башни Ворона", - "research_provenance": [ - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 3, - 7, - 1, - 3, - 1 - ], - "links": [ - [ - 0, - 0, - 0, - -1, - 0 - ], - [ - -1, - 0, - 1, - -1, - 1 - ], - [ - 1, - 0, - 0, - -1, - -1 - ], - [ - 0, - 0, - 0, - 0, - 1 - ], - [ - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 9, - "baselineCommands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 37, - "plateSwitches": 8, - "commands": [ - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 5.393829000000551, - "median": 5.431555999999546, - "max": 5.844538000001194 - } - } - }, - { - "id": "raven-tower-floor4-elixirs-chest", - "title": "Сундук с эликсирами (4-й этаж башни)", - "titleEn": "Elixir chest (Raven Tower floor 4)", - "url": "https://unlockmyloot.com/locks/raven-tower-floor4-elixirs-chest/", - "code": "h24ZgUAAEAQgAwAKQAA", - "flip": false, - "description": "4-й этаж за лестницей - зелья и экстракты", - "catalogMetadata": "7 плиток · 36 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 609, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/raven-tower-floor4-elixirs-chest/index.html", - "en/locks/raven-tower-floor4-elixirs-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 159, - "research_section": "### 5j. Сундук с эликсирами на 4-м этаже башни Ворона", - "research_provenance": [ - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 4, - 6, - 6, - 7, - 1, - 7, - 4 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - -1, - -1, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - -1, - 0, - 0, - 0, - 0, - 0 - ], - [ - -1, - 0, - 1, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 1, - 1, - 0, - 0 - ], - [ - -1, - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 8, - "baselineCommands": [ - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 108.15463799999998, - "median": 113.79403699999966, - "max": 127.23316599999816 - } - } - }, - { - "id": "raven-tower-floor4-food-chest", - "title": "Сундук с едой (4-й этаж башни)", - "titleEn": "Food chest (Raven Tower floor 4)", - "url": "https://unlockmyloot.com/locks/raven-tower-floor4-food-chest/", - "code": "aYqYiAQgCACABA", - "flip": false, - "description": "4-й этаж слева от эликсиров - вино, пиво, мясо", - "catalogMetadata": "6 плиток · 43 хода · 7 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 615, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/raven-tower-floor4-food-chest/index.html", - "en/locks/raven-tower-floor4-food-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 165, - "research_section": "### 5k. Сундук с едой на 4-м этаже башни Ворона", - "research_provenance": [ - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 5, - 7, - 2, - 3, - 5, - 7 - ], - "links": [ - [ - 0, - 0, - -1, - 0, - -1, - 0 - ], - [ - 0, - 0, - 0, - 1, - 0, - 0 - ], - [ - -1, - 0, - 0, - 0, - 0, - 0 - ], - [ - -1, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - -1, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 11, - "baselineCommands": [ - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 51.97651599999881, - "median": 53.12884100000156, - "max": 56.046502999997756 - } - } - }, - { - "id": "raven-tower-top-chest", - "title": "Сундук на вершине башни Ворона", - "titleEn": "Chest at the top of Raven Tower", - "url": "https://unlockmyloot.com/locks/raven-tower-top-chest/", - "code": "aMCmkAAGBBYAAA", - "flip": false, - "description": "Верх башни - кольцо ловкости, зов сна, велайис", - "catalogMetadata": "6 плиток · 48 ходов · 9 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 621, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/raven-tower-top-chest/index.html", - "en/locks/raven-tower-top-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 171, - "research_section": "### 5l. Сундук на вершине башни Ворона", - "research_provenance": [ - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 5, - 4, - 1, - 1, - 6, - 2 - ], - "links": [ - [ - 0, - -1, - -1, - 1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - -1, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0, - 1, - 1 - ], - [ - -1, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 20, - "baselineCommands": [ - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 42.69402500000069, - "median": 45.8797799999993, - "max": 46.05100699999821 - } - } - }, - { - "id": "fire-lizard-cave-chest", - "title": "Ящик у огненного ящера", - "titleEn": "Fire lizard cave box", - "url": "https://unlockmyloot.com/?lock=aDasBgBgCAACCA", - "code": "aDasBgBgCAACCA", - "flip": false, - "description": "Первый ящик в пещерке у круга с огненным ящером на востоке", - "catalogMetadata": "6 плиток · 41 ход · 7 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 628, - "english_catalog": "en/locks/index.html", - "landing_pages": [], - "source_notes": "locks-research.md", - "supplied_by": "Влад · DTF", - "research_line": 311, - "research_section": "### 6. Ящик у огненного ящера (восток)", - "research_provenance": [ - "- Нашёл и прислал: Влад, DTF (https://dtf.ru/id644393). СТАТУС: в каталоге (6-я карточка)." - ] - }, - "definition": { - "state": [ - 5, - 1, - 7, - 7, - 6, - 4 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 1, - -1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 1 - ], - [ - -1, - 0, - 0, - 0, - 0, - 0 - ], - [ - -1, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - -1 - ], - [ - 0, - 0, - -1, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 10, - "baselineCommands": [ - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 52.59948500000246, - "median": 53.04343399999925, - "max": 66.56672799999797 - } - } - }, - { - "id": "silas-tavern-basement-chest", - "title": "Сундук в подвале таверны", - "titleEn": "Silas tavern basement chest", - "url": "https://unlockmyloot.com/locks/silas-tavern-basement-chest/", - "code": "RrGUCRKQIQ", - "flip": false, - "description": "Подводный сундук в подвале под таверной Силаса - топор Брана, квест «По шею»", - "catalogMetadata": "5 плиток · 30 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 635, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/silas-tavern-basement-chest/index.html", - "en/locks/silas-tavern-basement-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 319, - "research_section": "### 7. Сундук в подвале таверны (Новый лагерь, таверна Силаса)", - "research_provenance": [ - "- СТАТУС: в каталоге (7-я карточка)." - ] - }, - "definition": { - "state": [ - 4, - 3, - 7, - 2, - 5 - ], - "links": [ - [ - 0, - -1, - -1, - 0, - 0 - ], - [ - 1, - 0, - 0, - -1, - 0 - ], - [ - -1, - 1, - 0, - 1, - 0 - ], - [ - -1, - 0, - 0, - 0, - 1 - ], - [ - 0, - 0, - -1, - 0, - 0 - ] - ] - }, - "expectedActions": 9, - "baselineCommands": [ - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 3.8142619999998715, - "median": 3.8800910000027216, - "max": 3.9581380000017816 - } - } - }, - { - "id": "silas-tavern-storeroom-chest", - "title": "Сундук склада таверны", - "titleEn": "Tavern storeroom chest", - "url": "https://unlockmyloot.com/?lock=Yt05ARCBAAARCA", - "code": "Yt05ARCBAAARCA", - "flip": false, - "description": "Сундук справа в складе таверны за дверью за Силасом (бармен) - это не подвал", - "catalogMetadata": "6 плиток · 31 ход · 8 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 641, - "english_catalog": "en/locks/index.html", - "landing_pages": [], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 329, - "research_section": "### 8. Сундук склада таверны (Новый лагерь, за дверью Силаса)", - "research_provenance": [ - "- СТАТУС: в каталоге (8-я карточка)." - ] - }, - "definition": { - "state": [ - 2, - 4, - 4, - 6, - 2, - 7 - ], - "links": [ - [ - 0, - 1, - 0, - 0, - 0, - 1 - ], - [ - 0, - 0, - 1, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0, - 0, - 1 - ], - [ - 0, - 0, - -1, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 9, - "baselineCommands": [ - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 30.20962199999849, - "median": 31.75662200000079, - "max": 36.70291599999837 - } - } - }, - { - "id": "silas-tavern-storeroom-door", - "title": "Склад таверны - дверь", - "titleEn": "Tavern storeroom door", - "url": "https://unlockmyloot.com/?lock=aSnYABCIZIICCA", - "code": "aSnYABCIZIICCA", - "flip": false, - "description": "Запертая дверь в склад таверны Нового лагеря за барменом Силасом", - "catalogMetadata": "6 плиток · 31 ход · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 647, - "english_catalog": "en/locks/index.html", - "landing_pages": [], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 338, - "research_section": "### 9. Дверь в склад таверны (Новый лагерь, за Силасом)", - "research_provenance": [ - "- СТАТУС: в каталоге (9-я карточка)." - ] - }, - "definition": { - "state": [ - 5, - 5, - 6, - 2, - 7, - 7 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0, - 0, - -1 - ], - [ - 0, - -1, - 0, - 0, - 1, - -1 - ], - [ - 1, - 0, - -1, - 0, - 0, - 0 - ], - [ - -1, - 0, - 0, - 0, - 0, - -1 - ], - [ - 0, - 0, - -1, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 10, - "baselineCommands": [ - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 22.321948999997403, - "median": 23.63271099999838, - "max": 27.04742599999736 - } - } - }, - { - "id": "jan-pond-house-chest", - "title": "Сундук в домике напротив Джана", - "titleEn": "Hut chest opposite Jan (New Camp)", - "url": "https://unlockmyloot.com/?lock=aDAIAEhAAAQaAA", - "code": "aDAIAEhAAAQaAA", - "flip": false, - "description": "Домик напротив Джана у пруда рядом с Новым лагерем - нужно переплыть; внутри чертёж «Дробителя камней» (требует кузнечное дело)", - "catalogMetadata": "6 плиток · 33 хода · 7 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 653, - "english_catalog": "en/locks/index.html", - "landing_pages": [], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 372, - "research_section": "### Сундук в домике напротив Джана (Новый лагерь)", - "research_provenance": [ - "- Источник: Лёша (прошёл). Джан — тренер кулачного боя у Нового лагеря" - ] - }, - "definition": { - "state": [ - 5, - 1, - 7, - 1, - 1, - 3 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - -1, - 0, - 1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 1 - ], - [ - 0, - 0, - 1, - -1, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 8, - "baselineCommands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 48.03793500000029, - "median": 48.69068799999877, - "max": 50.139368000000104 - } - } - }, - { - "id": "graham-chest", - "title": "Сундук Грэхэма", - "titleEn": "Graham's chest", - "url": "https://unlockmyloot.com/locks/graham-chest/", - "code": "bE0IBAAQkBCAEg", - "flip": false, - "description": "Сундук картографа Грэхэма в Старом лагере - у хижины слева от Северных ворот, где покупают карту колонии за руду", - "catalogMetadata": "6 плиток · 40 ходов · 8 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 659, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/graham-chest/index.html", - "en/locks/graham-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 384, - "research_section": "### Сундук Грэхэма (Старый лагерь)", - "research_provenance": [ - "- Источник: Грэхэм — старатель/картограф Старого лагеря, живёт в хижине слева" - ] - }, - "definition": { - "state": [ - 7, - 2, - 2, - 6, - 1, - 3 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 1, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0, - -1, - 1 - ], - [ - 0, - 0, - 0, - 0, - 1, - 0 - ], - [ - 0, - -1, - 0, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - -1, - 0, - 0 - ] - ] - }, - "expectedActions": 10, - "baselineCommands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 32.42070899999817, - "median": 33.52989900000102, - "max": 36.52016999999978 - } - } - }, - { - "id": "gravo-chest", - "title": "Сундук Граво", - "titleEn": "Gravo's chest", - "url": "https://unlockmyloot.com/?lock=SkCAAjAhAg", - "code": "SkCAAjAhAg", - "flip": false, - "description": "Сундук старателя Граво в Старом лагере - его хижина у входа в замок (Северные ворота), напротив дома Диего; Граво правит репутацию", - "catalogMetadata": "5 плиток · 27 ходов · 6 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 665, - "english_catalog": "en/locks/index.html", - "landing_pages": [], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 396, - "research_section": "### Сундук Граво (Старый лагерь)", - "research_provenance": [ - "- Источник: Граво — старатель Старого лагеря, хижина у входа в замок / у Северных" - ] - }, - "definition": { - "state": [ - 6, - 2, - 1, - 1, - 5 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0, - 1 - ], - [ - -1, - 0, - 0, - 0, - 1 - ], - [ - 0, - 0, - -1, - 0, - 0 - ], - [ - 0, - 1, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 8, - "baselineCommands": [ - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 5.467348999998649, - "median": 8.080862999999226, - "max": 8.312551999999414 - } - } - }, - { - "id": "diego-chest", - "title": "Сундук Диего", - "titleEn": "Diego's chest", - "url": "https://unlockmyloot.com/locks/diego-chest/", - "code": "Yix4ABIAASCSAE", - "flip": false, - "description": "Сундук Диего в Старом лагере - его хижина юго-восточнее ворот замка, с красной тканью и шкурами у входа; внутри отмычки, эссенция исцеления и руда", - "catalogMetadata": "6 плиток · 40 ходов · 8 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 671, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/diego-chest/index.html", - "en/locks/diego-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 407, - "research_section": "### Сундук Диего (Старый лагерь)", - "research_provenance": [ - "- Источник: Диего — ключевой NPC (Тень), помогает герою в начале. Хижина юго-" - ] - }, - "definition": { - "state": [ - 2, - 1, - 6, - 5, - 4, - 7 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0, - -1, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 1, - 0, - 0, - -1, - 0 - ], - [ - 0, - -1, - 1, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0, - 1, - 0 - ] - ] - }, - "expectedActions": 11, - "baselineCommands": [ - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 30.864245999997365, - "median": 31.402186000002985, - "max": 48.39241899999979 - } - } - }, - { - "id": "grim-chest", - "title": "Сундук Грима", - "titleEn": "Grim's chest", - "url": "https://unlockmyloot.com/?lock=aFhwAAABhgAAKY", - "code": "aFhwAAABhgAAKY", - "flip": false, - "description": "Сундук старателя Грима в Старом лагере - рядом с Диего у ворот замка (слева от него); завязан на квест «Амулет»", - "catalogMetadata": "6 плиток · 21 ход · 8 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 677, - "english_catalog": "en/locks/index.html", - "landing_pages": [], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 419, - "research_section": "### Сундук Грима (Старый лагерь)", - "research_provenance": [ - "- Источник: Грим — старатель Старого лагеря, живёт рядом с Диего у ворот замка" - ] - }, - "definition": { - "state": [ - 5, - 2, - 4, - 1, - 4, - 5 - ], - "links": [ - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 1, - -1, - 0 - ], - [ - 1, - -1, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - -1, - -1, - 1, - -1, - 0 - ] - ] - }, - "expectedActions": 8, - "baselineCommands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 20.744904999999562, - "median": 21.38251400000081, - "max": 25.32780400000047 - } - } - }, - { - "id": "mercenary-near-grim-chest", - "title": "Сундук наёмника (у Грима)", - "titleEn": "Mercenary's chest (by Grim)", - "url": "https://unlockmyloot.com/?lock=ZA4gEWAAAAASAQ", - "code": "ZA4gEWAAAAASAQ", - "flip": false, - "description": "Сундук наёмника слева от Грима в Старом лагере, рядом с Диего у ворот замка", - "catalogMetadata": "6 плиток · 25 ходов · 7 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 683, - "english_catalog": "en/locks/index.html", - "landing_pages": [], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 430, - "research_section": "### Сундук наёмника у Грима (Старый лагерь)", - "research_provenance": [ - "- Источник: Лёша. Безымянный наёмник слева от Грима. Имени в гайдах нет —" - ] - }, - "definition": { - "state": [ - 3, - 1, - 2, - 7, - 2, - 1 - ], - "links": [ - [ - 0, - 0, - 0, - 1, - 0, - 1 - ], - [ - 1, - 0, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 1, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 1, - 0, - 0 - ] - ] - }, - "expectedActions": 8, - "baselineCommands": [ - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 30.25687200000175, - "median": 31.47373399999924, - "max": 35.279522999997425 - } - } - }, - { - "id": "popular-lock", - "title": "Популярный замок", - "titleEn": "Popular lock", - "url": "https://unlockmyloot.com/locks/popular-lock/", - "code": "TAmBLEwMAE", - "flip": false, - "description": "Частый замок из копилки - полный набор связей в коде", - "catalogMetadata": "5 плиток · 46 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 689, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/popular-lock/index.html", - "en/locks/popular-lock/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": null, - "research_line": 269, - "research_section": "### 5w. Популярный замок (5 плиток)", - "research_provenance": [ - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 7, - 1, - 2, - 2, - 5 - ], - "links": [ - [ - 0, - 0, - 0, - -1, - 1 - ], - [ - 1, - 0, - -1, - 0, - -1 - ], - [ - 1, - -1, - 0, - 0, - 0 - ], - [ - 1, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - -1, - 0 - ] - ] - }, - "expectedActions": 13, - "baselineCommands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 6.609628999998677, - "median": 8.722789999999804, - "max": 12.860713000001851 - } - } - }, - { - "id": "alberto-mine-hut-chest", - "title": "Сундук в хижине шахты Альберто", - "titleEn": "Chest in Alberto's mine hut", - "url": "https://unlockmyloot.com/locks/alberto-mine-hut-chest/", - "code": "ULWBAQhAKg", - "flip": true, - "description": "1-й этаж хижины у шахты Альберто", - "catalogMetadata": "5 плиток · 36 ходов · 7 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 695, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/alberto-mine-hut-chest/index.html", - "en/locks/alberto-mine-hut-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": "nepetacataria · DTF", - "research_line": 277, - "research_section": "### 5x. Сундук в хижине шахты Альберто", - "research_provenance": [ - "- Источник: nepetacataria, DTF (https://dtf.ru/id3493234), [комментарий](https://dtf.ru/games/5121406-kak-vskryt-zamki-v-gothic-1-remake?comment=65516715).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 1, - 3, - 7, - 6, - 5 - ], - "links": [ - [ - 0, - 0, - 0, - -1, - 0 - ], - [ - 0, - 0, - 0, - -1, - 0 - ], - [ - 1, - 0, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0, - 1 - ], - [ - 1, - 1, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 9, - "baselineCommands": [ - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 5.769542999998521, - "median": 6.056866999999329, - "max": 6.331880999998248 - } - } - }, - { - "id": "alberto-mine-upstairs-left-chest", - "title": "Левый сундук наверху в шахте Альберто", - "titleEn": "Left chest upstairs in Alberto's mine", - "url": "https://unlockmyloot.com/locks/alberto-mine-upstairs-left-chest/", - "code": "MEAIAho", - "flip": true, - "description": "Наверху по лестнице - левый сундук", - "catalogMetadata": "4 плитки · 20 ходов · 5 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 702, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/alberto-mine-upstairs-left-chest/index.html", - "en/locks/alberto-mine-upstairs-left-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": "nepetacataria · DTF", - "research_line": 284, - "research_section": "### 5y. Левый сундук наверху в шахте Альберто", - "research_provenance": [ - "- Источник: nepetacataria · DTF (тот же комментарий).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 1, - 2, - 1, - 1 - ], - "links": [ - [ - 0, - 0, - 0, - -1 - ], - [ - 0, - 0, - 0, - 0 - ], - [ - 0, - -1, - 0, - 0 - ], - [ - 1, - -1, - -1, - 0 - ] - ] - }, - "expectedActions": 4, - "baselineCommands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 0.34879500000170083, - "median": 0.37524499999926775, - "max": 0.3974180000004708 - } - } - }, - { - "id": "alberto-mine-upstairs-right-chest", - "title": "Правый сундук наверху в шахте Альберто", - "titleEn": "Right chest upstairs in Alberto's mine", - "url": "https://unlockmyloot.com/locks/alberto-mine-upstairs-right-chest/", - "code": "UZ1wAFAKxU", - "flip": true, - "description": "Наверху по лестнице - правый, короткий замок", - "catalogMetadata": "5 плиток · 13 ходов · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 709, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/alberto-mine-upstairs-right-chest/index.html", - "en/locks/alberto-mine-upstairs-right-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": "nepetacataria · DTF", - "research_line": 291, - "research_section": "### 5z. Правый сундук наверху в шахте Альберто", - "research_provenance": [ - "- Источник: nepetacataria · DTF.", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 1, - 7, - 4, - 6, - 4 - ], - "links": [ - [ - 0, - -1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - -1 - ], - [ - -1, - 0, - 0, - 0, - 0 - ], - [ - 1, - 1, - 1, - 0, - -1 - ], - [ - 0, - -1, - -1, - -1, - 0 - ] - ] - }, - "expectedActions": 5, - "baselineCommands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 1.5827119999994466, - "median": 1.7594979999994393, - "max": 1.899319000000105 - } - } - }, - { - "id": "cavalorn-storeroom-door", - "title": "Дверь на склад охотника Кавалорна", - "titleEn": "Hunter Cavalorn's storeroom door", - "url": "https://unlockmyloot.com/locks/cavalorn-storeroom-door/", - "code": "cuaSQAAQgoQJQA", - "flip": true, - "description": "Склад охотника Кавалорна", - "catalogMetadata": "6 плиток · 41 ход · 10 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 716, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/cavalorn-storeroom-door/index.html", - "en/locks/cavalorn-storeroom-door/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": "nepetacataria · DTF", - "research_line": 298, - "research_section": "### 5aa. Дверь на склад охотника Кавалорна", - "research_provenance": [ - "- Источник: nepetacataria · DTF.", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 2, - 4, - 5, - 7, - 5, - 5 - ], - "links": [ - [ - 0, - -1, - 1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0, - -1, - 0 - ], - [ - 0, - -1, - -1, - 0, - 0, - 1 - ], - [ - 0, - 0, - 0, - -1, - 0, - 1 - ], - [ - 1, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 11, - "baselineCommands": [ - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 40.57944999999745, - "median": 42.616525000001275, - "max": 43.05461400000058 - } - } - }, - { - "id": "old-to-new-camp-bandits-chest", - "title": "Сундук у разбойников на пути в Новый лагерь", - "titleEn": "Chest by bandits on the path to the New Camp", - "url": "https://unlockmyloot.com/locks/old-to-new-camp-bandits-chest/", - "code": "cOEMSCgBGIQAAA", - "flip": true, - "description": "Двое разбойников на тропе Старый → Новый лагерь", - "catalogMetadata": "6 плиток · 33 хода · 9 связей", - "provenance": { - "commit": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "catalog": "locks/index.html", - "catalog_line": 723, - "english_catalog": "en/locks/index.html", - "landing_pages": [ - "locks/old-to-new-camp-bandits-chest/index.html", - "en/locks/old-to-new-camp-bandits-chest/index.html" - ], - "source_notes": "locks-research.md", - "supplied_by": "nepetacataria · DTF", - "research_line": 305, - "research_section": "### 5ab. Сундук у разбойников на пути в Новый лагерь", - "research_provenance": [ - "- Источник: nepetacataria · DTF (автор уточнила направление: не в Старый, а в Новый лагерь).", - "- СТАТУС: в каталоге." - ] - }, - "definition": { - "state": [ - 1, - 4, - 5, - 2, - 1, - 4 - ], - "links": [ - [ - 0, - 0, - 1, - 0, - -1, - 0 - ], - [ - 0, - 0, - -1, - -1, - 0, - 0 - ], - [ - 0, - 0, - 0, - 1, - 0, - 1 - ], - [ - -1, - 0, - -1, - 0, - 0, - 1 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0, - 0, - 0 - ] - ] - }, - "expectedActions": 10, - "baselineCommands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - } - ], - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 24.668550999998843, - "median": 24.905797999999777, - "max": 25.209614999999758 - } - } - } -] diff --git a/benchmarks/fixtures/manifest.json b/benchmarks/fixtures/manifest.json deleted file mode 100644 index 2b4ce93..0000000 --- a/benchmarks/fixtures/manifest.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "schemaVersion": 1, - "count": 45, - "catalogSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", - "source": { - "repository": "https://github.com/1h8s/unlockmyloot", - "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "files": { - "en/locks/alberto-mine-hut-chest/index.html": "b80235a81fa161a90268a69daf2a3e53e4a3d82c70e5ef88b5e46fb2aff3c2eb", - "en/locks/alberto-mine-upstairs-left-chest/index.html": "2e6b78e62b599c297d7d6c7c9b615dec997bb4a6131705e6cc800c9b95dcc9d1", - "en/locks/alberto-mine-upstairs-right-chest/index.html": "4181816e8bb3ac662cf2aa5851bc5bfb83ecca101871b747a484e7fa7a4f2bc6", - "en/locks/arlin-dungeon-key/index.html": "d8a1b2371575e7401ec878f77484570a433aa4c36e5fd45ad204209f0afd0040", - "en/locks/arto-far-bed-chest/index.html": "07aaefc05cb0fdf0c7ee52e9c4fa480b77432fb88451ca205a73f3380c657bd0", - "en/locks/arto-near-bed-chest/index.html": "1e2aeb0a1eb27aefbe2561f7f10b42e7de314d9e6c968293ccf112be0f25f621", - "en/locks/arto-room-door/index.html": "a05c7f12071be89f0066f98e2659f40a540e2938d70972c12cd2da654e0b643f", - "en/locks/cavalorn-storeroom-door/index.html": "2f0d4ad2b762ee13abdba52cd2d95f0092737918e32028912137dc13b58b1a78", - "en/locks/cor-galom-bedroom-chest/index.html": "35b34337121619859be49315134ad706ccf9ffa83088fac03e62bbd92a94142c", - "en/locks/cor-galom-chest/index.html": "01c18921c3123fba5335ead0914c7c0b8ed1d427b42e1b0439e8c330ae6ac64b", - "en/locks/diego-chest/index.html": "c9374172928844e753687d63396e11b6eb2d699a90e171f1182e6590a465126b", - "en/locks/gomez-bedrooms-door/index.html": "2b98750357a041e4af80630b1993f95088a471afbd5e336e24bb0ac7be3e40fd", - "en/locks/gomez-bedrooms-far-chest/index.html": "3ca12f2c54056c391c8d95b987ae77536faed0cbfeb08c976bb73c706f328928", - "en/locks/gomez-bedrooms-near-chest/index.html": "d87b2e949d34e50f9cadf71d111c840b32e9e7a594060c9be19039785cc637f9", - "en/locks/gomez-bedside-chest/index.html": "e1cd6ae0053d044e513a3b45eb11f8f13b0c3302b62f3a31e77eed1f8e83fa92", - "en/locks/gomez-chambers-door/index.html": "e00302b7b8084911def5691d1e7aff17cfb24cfdc3dbaa65e1a0c7e7d4e43c50", - "en/locks/gomez-throne-hall-chest/index.html": "9c921b14877b3c7f5900045c3d4112bcff57c5949a63b2036e85616f1d4e6fb9", - "en/locks/gomez-wall-chest/index.html": "48bff3709e568b823b5b4f40b846de0ae3ebb8112ed1e59899a64b8975f1f05c", - "en/locks/gomez-wine-cellar/index.html": "e91ab32910e8f6a9478b4d4b0cc38ab6fc5223a8ae0722656f3f59e06c47bd12", - "en/locks/graham-chest/index.html": "921b8a106183a1ae260c7dd69971c78c995f9debd64a516a370bcd93cbad5d73", - "en/locks/index.html": "a1c838d45f57fa9b9ffa8f8e69017c17b2fe8da5431b00f1c37df0e0a688c9d0", - "en/locks/old-camp-dungeon-door/index.html": "54e4e0e60a41e9ee9a5412625ce4d9f4ccc186645c4bf352faaa9fa2f0df3c2e", - "en/locks/old-to-new-camp-bandits-chest/index.html": "820d8e8a93fb33135ad95897b6431b664e43c88ae22dd9f4b67be85d79761b12", - "en/locks/popular-lock/index.html": "2c3011e509fe81819262a0a26accc5bd5d049f231dd19a3f38899b435a794a35", - "en/locks/raven-tower-floor2-chest-2/index.html": "b8e7fa53c36e9066f0752420c904f00fd0be8c3f6e5040db0204a138495613a8", - "en/locks/raven-tower-floor2-stairs-chest/index.html": "3a75e3a01b3817ec4cec0f2f994d6ddae7506220a8f63d509e4ad58e328fcb17", - "en/locks/raven-tower-floor3-empty-chest/index.html": "79298f343f8b35201cf848b56ee6caa89f6053fa8f059745305a52598ed6a325", - "en/locks/raven-tower-floor3-entrance-chest/index.html": "295d2c791cebc9762b50b2d3d0171fe5e17ffb0b1dffb85dfd2eb74584f96de9", - "en/locks/raven-tower-floor4-elixirs-chest/index.html": "30a8049b0e03084984f28b6009d498958deebc20fc62c0b5a10f75602986018c", - "en/locks/raven-tower-floor4-food-chest/index.html": "614cc96c33fa62595f6d80cc6ddcb13a32234c1741f2b4517fb4b652929dd4e4", - "en/locks/raven-tower-left-chest/index.html": "fdadb796e0359dd662f5cac35e1fd7a5c6b8a2d97b47ea4705e16ffab3c4947f", - "en/locks/raven-tower-right-chest/index.html": "dc0784a332bbcd7da39306fcf154ccdf906251179dc73201af0aa385907707ca", - "en/locks/raven-tower-top-chest/index.html": "e29553bcf88d46241dc0d5e90504d8b3a760c970c585a233b8d8a83ea2d23473", - "en/locks/rice-lord-storeroom/index.html": "4c618c74718b466872be4ba378967334c16b6fdd1b9470c0bcc64bc7b2c440ad", - "en/locks/ruined-tower-door/index.html": "8b74fbcf81234090bfada0cdd7fd33cbed7f1221cd8371ecbb43e582301de0e7", - "en/locks/scatty-chest/index.html": "7775f24351a5d05255e6440851e5522ca696f71c8c8d2e19a0d48735ba5e5ed9", - "en/locks/silas-tavern-basement-chest/index.html": "78e6e411c056df5828e52e4fc488323d60a9ae0b7eeb8e764189074162cb0798", - "en/locks/swamp-camp-forge-chest/index.html": "f5124edab177b2052d16463965e1c9428e6b24ce98d0da72653f40106c63baf2", - "en/locks/voran-bedside-chest/index.html": "caa5bbe7c3da95a8e4ddffd8f646c9d11b35e41d37a0939746547ea1ddd26376", - "index.html": "691f744099a9169f30e9883fddc5fdbb4949d306f3011e601678419d7b93821b", - "locks-research.md": "b1d4ca78220e225fabb32f6e26d10d1b66dc38fed4eb8747bebd0998d9222eaa", - "locks/alberto-mine-hut-chest/index.html": "1903860a6608eec79df1bbbf8c6710a9f857f5153a4f591532ae9f08138942c5", - "locks/alberto-mine-upstairs-left-chest/index.html": "05ca7aabafa7dba63365d93bdd5fb186d46700fa1a1a9a2260117c175dc17f2b", - "locks/alberto-mine-upstairs-right-chest/index.html": "0c15a419ebf82a572eb5119dbd213bf6df4be1731fc4d5479aa29ecde4058c94", - "locks/arlin-dungeon-key/index.html": "f0e15522b7a6ed7faf2c9d77f5e59fee1a1ea07061ecff8f50f28aef27f8295b", - "locks/arto-far-bed-chest/index.html": "22febd161e0dd560e95c62b3583a3beb8d6558d5023f5e20ebc86cbd302b4ff5", - "locks/arto-near-bed-chest/index.html": "224b8cee55849b7dabd527f0b34240a521e217e3158d69c11e14e06933ede94a", - "locks/arto-room-door/index.html": "d7530ade288e42a64ebea72e141d4814727152f83b691857b3a20bab5182adb8", - "locks/cavalorn-storeroom-door/index.html": "2423004cf2a361a68619246d60fd1303c12f61400443c674d300866c7c1850b2", - "locks/cor-galom-bedroom-chest/index.html": "92d4dc4ad5ede6aa39675230223d1cc4370adf5b88cc29f91d22493acd93e394", - "locks/cor-galom-chest/index.html": "63e943853e7b2e2c419ace53cd0b38062b16e04d2dd5c523266d69fc4c30c4c1", - "locks/diego-chest/index.html": "09244d9c13a3093202dd3e294e620a78e77845ffe9360bbe13313da6ef0fea0c", - "locks/gomez-bedrooms-door/index.html": "22c1c125225095d1d9e6042543dd051b3951ba070fedee8b0e12a0da59243615", - "locks/gomez-bedrooms-far-chest/index.html": "df38114cec6f8b9b8fd4742a85e0dc1846adc6d303cd2740bff4491ead62a635", - "locks/gomez-bedrooms-near-chest/index.html": "95023d568f4f0b19f7dff3d0cf6c203f513f12154cf1ba259f257acce0883297", - "locks/gomez-bedside-chest/index.html": "eed3a69f01a57ec2899c7d75c61b442963140633501e21dbaa9ef983808d9c6d", - "locks/gomez-chambers-door/index.html": "b99eb923fc6b59b2d946e0a7fc06875bee1742e73ded25d2c8d38e56c6f5a583", - "locks/gomez-throne-hall-chest/index.html": "c526b2dd13572ab050f3df72626b356d87c4ddf5587dbfda90e592f8a935bde3", - "locks/gomez-wall-chest/index.html": "37435fe39e8564fe41eecc23a844ff30973959cea117bc54db0f3282064ea5a6", - "locks/gomez-wine-cellar/index.html": "2dd8197e796ed1ade7c15f56ea6ade1db696dc819f2d009a69dbf5f25aab6bee", - "locks/graham-chest/index.html": "413dc9ffd2a8dc90899d7b67e543c9688353ff73b5d8595c9415cdf462565f99", - "locks/index.html": "418950accf552db95cb037466215372bac10c2477aaddd90947342b6b87c0178", - "locks/old-camp-dungeon-door/index.html": "e8a4da8b4f9d0339576ec234d258e4e3af70f2c4bf083b5049482a4007ec448c", - "locks/old-to-new-camp-bandits-chest/index.html": "6dcbfa42ffcf8e6e9a7f32ad8a55ee5a16ec0b7c807a687b39e2aa51a8c21a1a", - "locks/popular-lock/index.html": "5433134fe4308f88fc652f0fcb6dcb8d61bd09a94520398d03d960a0cb788d71", - "locks/raven-tower-floor2-chest-2/index.html": "2ace199375b0865dc182fc23c612130837bd146e2992eb4b42296d142dd68afc", - "locks/raven-tower-floor2-stairs-chest/index.html": "6a0bdf73eead24e3458fbcbc87e48c42f13a18435010e2f1a31cd2b4671019d2", - "locks/raven-tower-floor3-empty-chest/index.html": "5dd67965ab6de1f441ba9d338d25220b00a68fedde9b2beb00decb752911cb91", - "locks/raven-tower-floor3-entrance-chest/index.html": "49e87c9e1e34652da272a0efcd628b42378f3f0cea8581838f59035707607598", - "locks/raven-tower-floor4-elixirs-chest/index.html": "cba32e3137d7c7becd2400bf15b75e6e4ef73c87fea5db7a1e89595cc19d3b9d", - "locks/raven-tower-floor4-food-chest/index.html": "713f3b65b250865775590854c4a52609ae80c6491c5bd1fc4c78d6b335235e5b", - "locks/raven-tower-left-chest/index.html": "44784aba6a21aa5883cb8d304b360264cc04cab1f371b624dd53c478b3bcba79", - "locks/raven-tower-right-chest/index.html": "8ab61196263291560d5f59a6fcdb8cf1a842711cdd6c1d70adc32d6ff9fbf44c", - "locks/raven-tower-top-chest/index.html": "eaeb59365f3a3a3a11dae19770697aec05c88e94643cc985a44f89de2c7e83db", - "locks/rice-lord-storeroom/index.html": "eff6425d39e668a60849ba863011517d6640e5a8ebdfabc7b81c8df0aed3186e", - "locks/ruined-tower-door/index.html": "2fc1bab43602d91fb30d10eea967258dcbdc051659db15f8736563fb0d5d1a8d", - "locks/scatty-chest/index.html": "18a6fa9f77b21ef668363288691fed5831c07ecda5ffd0a95d133b3929af59a9", - "locks/silas-tavern-basement-chest/index.html": "0e825fadee620075fdab0ea0f5d2ed754d94028b0f2890bdfe422dbe363187c8", - "locks/swamp-camp-forge-chest/index.html": "95bd46afd84e6dc241929060fccbf2e09f14e83f401352c07107861e12f318b6", - "locks/voran-bedside-chest/index.html": "c527200baac6eedadea1b6f4b3110326fe66fbc5183789f5b3d579b9513e9b0f" - }, - "solver": { - "path": "index.html", - "sha256": "691f744099a9169f30e9883fddc5fdbb4949d306f3011e601678419d7b93821b", - "blockSha256": "d5bc85f40fd7c5b61097403df399ecb7dfd2b58924dc7a670aa0d05306017380", - "startMarker": "/* solver-start", - "endMarker": "/* solver-end */" - } - }, - "importEvidence": { - "files": { - "catalog.json": "7e5ac1075ef6bc749e52a1393c3b1c49e53b14d106516f1bb67b2c3e4a410b7d", - "benchmark.json": "2e96ed1c560d051c1b5de2a13be74bee747795a796ebe2c44a6de5812d8b82d7", - "alternatives.json": "72f9c28f1c612ca752e6f5c0b2f9964dd38c8d267c36af6fc334d5f2f9f0f6bf" - }, - "expectedActions": "Exact grouped-command BFS from Apache baseline, cross-checked with optimized BFS and matrix prototype. Rechecked against current exact BFS in tests and every benchmark run.", - "historicalEnvironment": { - "date": "2026-09-07T06:02:47.149Z", - "node": "v24.19.0", - "cpu": "AMD EPYC 9V74 80-Core Processor", - "platform": "linux", - "arch": "x64", - "repetitions": 5 - }, - "historicalReference": "Historical timings are provenance only; current timing comparisons require an explicitly supplied verified reference checkout." - } -} diff --git a/benchmarks/reference-matrix/LICENSE b/benchmarks/reference-matrix/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/benchmarks/reference-matrix/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/benchmarks/reference-matrix/manifest.json b/benchmarks/reference-matrix/manifest.json deleted file mode 100644 index c7f090f..0000000 --- a/benchmarks/reference-matrix/manifest.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "schemaVersion": 1, - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", - "license": "Apache-2.0", - "files": { - "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", - "src/cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", - "src/index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "src/lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", - "src/matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", - "src/matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", - "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "src/search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", - "src/search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", - "src/solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", - "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" - }, - "notes": "Byte-identical Apache matrix reference, copied from source at the pinned revision. Benchmark-only; excluded from runtime and npm package. No external AGPL solver source." -} diff --git a/benchmarks/reference-matrix/src/cli.mjs b/benchmarks/reference-matrix/src/cli.mjs deleted file mode 100644 index f0b0efa..0000000 --- a/benchmarks/reference-matrix/src/cli.mjs +++ /dev/null @@ -1,36 +0,0 @@ -import { readFile, writeFile } from 'node:fs/promises'; -import { solveLock } from './index.mjs'; -import { parseLockDefinition } from './lock-definition.mjs'; -import { formatConsoleResult } from './result.mjs'; - -export function parseArgs(argv) { - const isPath = (value) => value.length > 0 && !value.startsWith('--'); - - if (argv.length === 1 && isPath(argv[0])) { - return { inputPath: argv[0], outputPath: null }; - } - if (argv.length === 3 && isPath(argv[0]) && argv[1] === '--output' && isPath(argv[2])) { - return { inputPath: argv[0], outputPath: argv[2] }; - } - throw new Error('Использование: node solve-lock.mjs [--output ]'); -} - -export async function runCli(argv, streams = {}) { - const stdout = streams.stdout ?? process.stdout; - const stderr = streams.stderr ?? process.stderr; - - try { - const { inputPath, outputPath } = parseArgs(argv); - const definition = parseLockDefinition(await readFile(inputPath, 'utf8')); - const result = solveLock(definition); - - if (outputPath !== null) { - await writeFile(outputPath, `${JSON.stringify(result, null, 2)}\n`, 'utf8'); - } - stdout.write(`${formatConsoleResult(result)}\n`); - return result.status === 'unsolvable' ? 2 : 0; - } catch (error) { - stderr.write(`Ошибка: ${error.message}\n`); - return 1; - } -} diff --git a/benchmarks/reference-matrix/src/index.mjs b/benchmarks/reference-matrix/src/index.mjs deleted file mode 100644 index 1f3d9ea..0000000 --- a/benchmarks/reference-matrix/src/index.mjs +++ /dev/null @@ -1,19 +0,0 @@ -import { validateLockDefinition } from './lock-definition.mjs'; -import { createSolveResult } from './result.mjs'; -import { searchBfs } from './search-bfs.mjs'; -import { searchMatrix } from './matrix-search.mjs'; -import { normalizeSearchOptions, prepareSearch, SearchBudget } from './search-limits.mjs'; - -export function solveLock(value, options) { - const limits = normalizeSearchOptions(options); - const definition = validateLockDefinition(value); - const prepared = prepareSearch(definition); - const budget = new SearchBudget(limits); - const commands = limits.algorithm === 'bfs' - ? searchBfs(prepared, budget) - : searchMatrix(definition, prepared, budget); - return createSolveResult(definition, commands); -} - -export { applyCommand, generateTransitions } from './transition.mjs'; -export { SearchLimitError } from './search-limits.mjs'; diff --git a/benchmarks/reference-matrix/src/lock-definition.mjs b/benchmarks/reference-matrix/src/lock-definition.mjs deleted file mode 100644 index 9e49bf1..0000000 --- a/benchmarks/reference-matrix/src/lock-definition.mjs +++ /dev/null @@ -1,60 +0,0 @@ -export class LockInputError extends Error { - constructor(message, options) { - super(message, options); - this.name = 'LockInputError'; - } -} - -export function validateLockDefinition(value) { - if (value === null || typeof value !== 'object' || Array.isArray(value)) { - throw new LockInputError('Определение замка должно быть JSON-объектом.'); - } - - const { state, links } = value; - if (!Array.isArray(state) || state.length < 2) { - throw new LockInputError('state должен содержать не менее 2 пластин.'); - } - - for (let index = 0; index < state.length; index += 1) { - const position = state[index]; - if (!Object.hasOwn(state, index) || !Number.isInteger(position) || position < 1 || position > 7) { - throw new LockInputError(`state[${index}] должен быть целым числом от 1 до 7.`); - } - } - - if (!Array.isArray(links) || links.length !== state.length) { - throw new LockInputError(`links должен быть матрицей ${state.length}x${state.length}.`); - } - - for (let rowIndex = 0; rowIndex < links.length; rowIndex += 1) { - const row = links[rowIndex]; - if (!Object.hasOwn(links, rowIndex) || !Array.isArray(row) || row.length !== state.length) { - throw new LockInputError(`links[${rowIndex}] должен содержать ${state.length} элементов.`); - } - for (let columnIndex = 0; columnIndex < row.length; columnIndex += 1) { - const coefficient = row[columnIndex]; - if (!Object.hasOwn(row, columnIndex) || ![-1, 0, 1].includes(coefficient)) { - throw new LockInputError(`links[${rowIndex}][${columnIndex}] должен быть -1, 0 или 1.`); - } - if (rowIndex === columnIndex && coefficient !== 0) { - throw new LockInputError(`links[${rowIndex}][${columnIndex}] на диагонали должен быть 0.`); - } - } - } - - return { - state: [...state], - links: links.map((row) => [...row]), - }; -} - -export function parseLockDefinition(text) { - try { - return validateLockDefinition(JSON.parse(text)); - } catch (error) { - if (error instanceof LockInputError) { - throw error; - } - throw new LockInputError(`Некорректный JSON: ${error.message}`, { cause: error }); - } -} diff --git a/benchmarks/reference-matrix/src/matrix-analysis.mjs b/benchmarks/reference-matrix/src/matrix-analysis.mjs deleted file mode 100644 index 56e48cb..0000000 --- a/benchmarks/reference-matrix/src/matrix-analysis.mjs +++ /dev/null @@ -1,64 +0,0 @@ -import { SearchLimitError } from './search-limits.mjs'; - -const abs = (value) => value < 0n ? -value : value; - -function fraction(n, d = 1n) { - if (n === 0n) return { n: 0n, d: 1n }; - if (d < 0n) { n = -n; d = -d; } - let a = abs(n); - let b = d; - while (b !== 0n) [a, b] = [b, a % b]; - return { n: n / a, d: d / a }; -} - -const divide = (a, b) => fraction(a.n * b.d, a.d * b.n); -const subtractProduct = (a, b, c) => fraction(a.n * b.d * c.d - b.n * c.n * a.d, a.d * b.d * c.d); - -/** - * Exact necessary equation A z = goal - state. links[source][target] is - * transposed into A[target][source]; selected-plate movement is the diagonal 1. - * Rational inconsistency or a fractional unique z is a proof of no integer path. - * Rank deficiency alone says nothing about whether a path exists. - */ -export function analyzeMatrix({ state, links }) { - const n = state.length; - const rows = Array.from({ length: n }, (_, target) => [ - ...Array.from({ length: n }, (_, source) => fraction(BigInt(source === target ? 1 : links[source][target]))), - fraction(BigInt(4 - state[target])), - ]); - const columns = []; - let rank = 0; - for (let column = 0; column < n; column += 1) { - let pivot = rank; - while (pivot < n && rows[pivot][column].n === 0n) pivot += 1; - if (pivot === n) continue; - [rows[rank], rows[pivot]] = [rows[pivot], rows[rank]]; - const divisor = rows[rank][column]; - for (let j = column; j <= n; j += 1) rows[rank][j] = divide(rows[rank][j], divisor); - for (let row = 0; row < n; row += 1) { - if (row === rank || rows[row][column].n === 0n) continue; - const factor = rows[row][column]; - for (let j = column; j <= n; j += 1) rows[row][j] = subtractProduct(rows[row][j], factor, rows[rank][j]); - } - columns.push(column); - rank += 1; - } - for (let row = rank; row < n; row += 1) { - if (rows[row][n].n !== 0n) return { kind: 'inconsistent', rank }; - } - if (rank < n) return { kind: 'singular', rank }; - const exact = Array(n); - for (let row = 0; row < n; row += 1) exact[columns[row]] = rows[row][n]; - if (exact.some((value) => value.d !== 1n)) return { kind: 'noninteger', rank }; - const net = exact.map(({ n: numerator }) => { - if (abs(numerator) > BigInt(Number.MAX_SAFE_INTEGER)) { - throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, String(numerator)); - } - return Number(numerator); - }); - const lowerBound = net.reduce((sum, value) => sum + Math.ceil(Math.abs(value) / 6), 0); - if (!Number.isSafeInteger(lowerBound)) { - throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, lowerBound); - } - return { kind: 'unique-integer', rank, net, lowerBound }; -} diff --git a/benchmarks/reference-matrix/src/matrix-search.mjs b/benchmarks/reference-matrix/src/matrix-search.mjs deleted file mode 100644 index a0ee348..0000000 --- a/benchmarks/reference-matrix/src/matrix-search.mjs +++ /dev/null @@ -1,167 +0,0 @@ -import { analyzeMatrix } from './matrix-analysis.mjs'; -import { searchBfs } from './search-bfs.mjs'; -import { commandFor, decodeDigits, movementLimits, SearchLimitError } from './search-limits.mjs'; - -/** One entry per open state; decrease-key never leaves obsolete heap nodes. */ -class IndexedHeap { - nodes = []; - - before(a, b) { - return a.g + a.h < b.g + b.h || - (a.g + a.h === b.g + b.h && (a.h < b.h || (a.h === b.h && a.sequence < b.sequence))); - } - - up(node) { - let index = node.heapIndex; - while (index > 0) { - const parent = Math.floor((index - 1) / 2); - if (!this.before(node, this.nodes[parent])) break; - this.nodes[index] = this.nodes[parent]; - this.nodes[index].heapIndex = index; - index = parent; - } - this.nodes[index] = node; - node.heapIndex = index; - } - - push(node) { - node.heapIndex = this.nodes.length; - this.nodes.push(node); - this.up(node); - } - - pop() { - const first = this.nodes[0]; - const last = this.nodes.pop(); - first.heapIndex = -1; - if (this.nodes.length === 0) return first; - let index = 0; - while (true) { - let child = index * 2 + 1; - if (child >= this.nodes.length) break; - if (child + 1 < this.nodes.length && this.before(this.nodes[child + 1], this.nodes[child])) child += 1; - if (!this.before(this.nodes[child], last)) break; - this.nodes[index] = this.nodes[child]; - this.nodes[index].heapIndex = index; - index = child; - } - this.nodes[index] = last; - last.heapIndex = index; - return first; - } -} - -/** - * A sufficient certificate, never an unreachability test. A legal single action - * for each nonzero net entry attains sum ceil(abs(net[i])/6), a global bound. - * An unsuccessful greedy attempt is discarded and full A* still runs. - */ -function greedyCertificate(prepared, net, budget) { - if (net.some((value) => Math.abs(value) > 6)) return null; - const remaining = [...net]; - const digits = new Uint8Array(prepared.n); - decodeDigits(prepared.source, digits); - const commands = []; - let pending = remaining.filter(Boolean).length; - while (pending > 0) { - let selected = -1; - for (let plate = 0; plate < prepared.n; plate += 1) { - const delta = remaining[plate]; - if (delta === 0) continue; - const limits = movementLimits(digits, prepared.effects[plate]); - if (Math.abs(delta) <= limits[delta > 0 ? 0 : 1]) { selected = plate; break; } - } - if (selected === -1) return null; - const delta = remaining[selected]; - const effect = prepared.effects[selected]; - for (let index = 0; index < effect.targets.length; index += 1) { - digits[effect.targets[index]] += delta * effect.signs[index]; - } - commands.push(commandFor(selected, delta)); - remaining[selected] = 0; - pending -= 1; - } - // Successful scheduling accounts for its path states and expansions too. - budget.check('maxVisited', commands.length + 1); - budget.check('maxExpanded', commands.length); - return commands; -} - -function reconstruct(node) { - const commands = []; - while (node.previous !== null) { - commands.push(commandFor(node.plate, node.delta)); - node = node.previous; - } - return commands.reverse(); -} - -/** - * Invertible A makes remaining net r unique for every physical state. An action - * changes one r[i] by at most six, hence h=sum ceil(abs(r[i])/6) is consistent. - * Closed nodes are final; the first popped goal has the minimum action count. - */ -export function searchMatrix(definition, prepared, budget) { - const { n, source, goal, effects } = prepared; - if (source === goal) { budget.visit(); return []; } - const analysis = analyzeMatrix(definition); - if (analysis.kind === 'inconsistent' || analysis.kind === 'noninteger') return null; - if (analysis.kind === 'singular') return searchBfs(prepared, budget); - const certificate = greedyCertificate(prepared, analysis.net, budget); - if (certificate !== null) return certificate; - - budget.visit(); - const root = { - key: source, g: 0, h: analysis.lowerBound, r: analysis.net, - previous: null, sequence: 0, heapIndex: -1, closed: false, - }; - const heap = new IndexedHeap(); - heap.push(root); - const records = new Map([[source, root]]); - const digits = new Uint8Array(n); - let sequence = 1; - while (heap.nodes.length > 0) { - const node = heap.pop(); - if (node.key === goal) return reconstruct(node); - budget.expand(); - node.closed = true; - decodeDigits(node.key, digits); - for (let plate = 0; plate < n; plate += 1) { - const limits = movementLimits(digits, effects[plate]); - const preferred = node.r[plate] < 0 ? -1 : 1; - for (let side = 0; side < 2; side += 1) { - const sign = side === 0 ? preferred : -preferred; - const limit = limits[sign > 0 ? 0 : 1]; - for (let steps = limit; steps >= 1; steps -= 1) { - const delta = sign * steps; - const key = node.key + delta * effects[plate].offset; - const g = node.g + 1; - const existing = records.get(key); - if (existing && (existing.closed || existing.g <= g)) continue; - if (existing) { - // The physical state uniquely determines r and h, so only g and - // the predecessor change. Its predecessor is already finalized. - existing.g = g; - existing.previous = node; - existing.plate = plate; - existing.delta = delta; - heap.up(existing); - continue; - } - budget.visit(); - budget.check('maxFrontier', heap.nodes.length + 1); - const r = [...node.r]; - r[plate] -= delta; - const h = node.h - Math.ceil(Math.abs(node.r[plate]) / 6) + Math.ceil(Math.abs(r[plate]) / 6); - if (!Number.isSafeInteger(r[plate]) || !Number.isSafeInteger(g + h)) { - throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, String(r[plate])); - } - const next = { key, g, h, r, previous: node, plate, delta, sequence: sequence++, heapIndex: -1, closed: false }; - records.set(key, next); - heap.push(next); - } - } - } - } - return null; -} diff --git a/benchmarks/reference-matrix/src/result.mjs b/benchmarks/reference-matrix/src/result.mjs deleted file mode 100644 index 03fd343..0000000 --- a/benchmarks/reference-matrix/src/result.mjs +++ /dev/null @@ -1,59 +0,0 @@ -import { createTargetState } from './state-codec.mjs'; -import { applyCommand } from './transition.mjs'; - -export function createSolveResult(definition, commands) { - const initialState = [...definition.state]; - const targetState = createTargetState(initialState.length); - - if (commands === null) { - return { status: 'unsolvable', initialState, targetState, commands: [] }; - } - - let finalState = [...initialState]; - for (const command of commands) { - const nextState = applyCommand(finalState, definition.links, command); - if (nextState === null) { - throw new Error('Решатель вернул заблокированную команду.'); - } - finalState = nextState; - } - - if (!finalState.every((position) => position === 4)) { - throw new Error('Решатель не привел замок к целевому состоянию.'); - } - - const plateSwitches = commands.reduce((count, command, index) => { - if (index === 0 || command.plate === commands[index - 1].plate) { - return count; - } - return count + 1; - }, 0); - - return { - status: 'solved', - initialState, - targetState, - commands: commands.map((command) => ({ ...command })), - finalState, - metrics: { - commands: commands.length, - divisions: commands.reduce((sum, command) => sum + command.steps, 0), - plateSwitches, - }, - }; -} - -export function formatConsoleResult(result) { - if (result.status === 'unsolvable') { - return 'Решение не найдено.'; - } - if (result.commands.length === 0) { - return 'Замок уже открыт.'; - } - - const directionNames = { left: 'влево', right: 'вправо' }; - const lines = result.commands.map((command, index) => ( - `${index + 1}. Пластина ${command.plate} - ${directionNames[command.direction]} x${command.steps}` - )); - return [`Найдено команд: ${result.commands.length}`, ...lines].join('\n'); -} diff --git a/benchmarks/reference-matrix/src/search-bfs.mjs b/benchmarks/reference-matrix/src/search-bfs.mjs deleted file mode 100644 index 1d79146..0000000 --- a/benchmarks/reference-matrix/src/search-bfs.mjs +++ /dev/null @@ -1,86 +0,0 @@ -import { decodeDigits, movementLimits, SearchLimitError } from './search-limits.mjs'; - -function unpack(packed) { - const plateDirection = Math.floor(packed / 6); - return { - plate: Math.floor(plateDirection / 2) + 1, - direction: plateDirection % 2 === 0 ? 'left' : 'right', - steps: packed % 6 + 1, - }; -} - -/** Exact macro BFS. Loop order is the original public tie-breaking contract. */ -export function searchBfs(prepared, budget) { - const { n, size, source, goal, effects } = prepared; - budget.visit(); - if (source === goal) return []; - - // Signed parents need two sentinel values. Queue stores only the live frontier. - const capacity = Math.min(size, budget.options.maxVisited, budget.options.maxFrontier); - const denseBytes = size * 5 + capacity * 4; - const dense = size <= 0x7fffffff && denseBytes <= budget.options.maxDenseBytes; - let parents; - let actions; - let queue; - try { - parents = dense ? new Int32Array(size).fill(-2) : new Map(); - actions = dense ? new Uint8Array(size) : null; - queue = dense ? new Int32Array(capacity) : []; - } catch (error) { - if (!(error instanceof RangeError)) throw error; - throw new SearchLimitError('maxDenseBytes', budget.options.maxDenseBytes, denseBytes, { cause: error }); - } - if (dense) parents[source] = -1; - else parents.set(source, { parent: -1, action: -1 }); - queue[0] = source; - let head = 0; - let tail = 1 % capacity; - let frontier = 1; - const digits = new Uint8Array(n); - - function reconstruct(code) { - const commands = []; - while (code !== source) { - if (dense) { - commands.push(unpack(actions[code])); - code = parents[code]; - } else { - const node = parents.get(code); - commands.push(unpack(node.action)); - code = node.parent; - } - } - return commands.reverse(); - } - - while (frontier > 0) { - budget.expand(); - const code = queue[head]; - if (!dense) queue[head] = undefined; - head = (head + 1) % capacity; - frontier -= 1; - decodeDigits(code, digits); - for (let plate = 0; plate < n; plate += 1) { - const limits = movementLimits(digits, effects[plate]); - for (let direction = 0; direction < 2; direction += 1) { - const sign = direction === 0 ? 1 : -1; - for (let steps = limits[direction]; steps >= 1; steps -= 1) { - const next = code + sign * steps * effects[plate].offset; - if (dense ? parents[next] !== -2 : parents.has(next)) continue; - budget.visit(); - const action = (plate * 2 + direction) * 6 + steps - 1; - if (dense) { - parents[next] = code; - actions[next] = action; - } else parents.set(next, { parent: code, action }); - if (next === goal) return reconstruct(next); - budget.check('maxFrontier', frontier + 1); - queue[tail] = next; - tail = (tail + 1) % capacity; - frontier += 1; - } - } - } - } - return null; -} diff --git a/benchmarks/reference-matrix/src/search-limits.mjs b/benchmarks/reference-matrix/src/search-limits.mjs deleted file mode 100644 index 7358aa4..0000000 --- a/benchmarks/reference-matrix/src/search-limits.mjs +++ /dev/null @@ -1,116 +0,0 @@ -/** Resource failures are deliberately distinct from a proof of unreachability. */ -export class SearchLimitError extends Error { - constructor(limit, maximum, used, options) { - super(`Превышен ресурсный предел поиска: ${limit} (предел ${maximum}, требуется ${used}).`, options); - this.name = 'SearchLimitError'; - this.limit = limit; - this.maximum = maximum; - this.used = used; - } -} - -const DEFAULTS = Object.freeze({ - maxVisited: 2_000_000, - maxExpanded: 1_000_000, - maxFrontier: 1_000_000, - maxDenseBytes: 64 * 1024 * 1024, -}); - -export function normalizeSearchOptions(options = {}) { - if (options === null || typeof options !== 'object' || Array.isArray(options)) { - throw new TypeError('Параметры поиска должны быть объектом.'); - } - const algorithm = options.algorithm === undefined ? 'matrix-astar' : options.algorithm; - if (algorithm !== 'matrix-astar' && algorithm !== 'bfs') { - throw new TypeError('algorithm должен быть matrix-astar или bfs.'); - } - const result = { algorithm }; - for (const [name, fallback] of Object.entries(DEFAULTS)) { - const value = options[name] === undefined ? fallback : options[name]; - const minimum = name === 'maxVisited' || name === 'maxFrontier' ? 1 : 0; - if (!Number.isSafeInteger(value) || value < minimum) { - throw new TypeError(`${name} должен быть безопасным целым числом не меньше ${minimum}.`); - } - result[name] = value; - } - return result; -} - -export class SearchBudget { - constructor(options) { - this.options = options; - this.visited = 0; - this.expanded = 0; - } - - check(name, used) { - if (used > this.options[name]) throw new SearchLimitError(name, this.options[name], used); - } - - visit() { - this.check('maxVisited', this.visited + 1); - this.visited += 1; - } - - expand() { - this.check('maxExpanded', this.expanded + 1); - this.expanded += 1; - } -} - -/** Validate encoding before matrix work, early returns, or dense allocation. */ -export function prepareSearch({ state, links }) { - const n = state.length; - const size = 7 ** n; - if (!Number.isSafeInteger(size)) { - throw new SearchLimitError('stateEncoding', Number.MAX_SAFE_INTEGER, size); - } - const powers = Array.from({ length: n }, (_, index) => 7 ** (n - index - 1)); - let source = 0; - let goal = 0; - for (let index = 0; index < n; index += 1) { - source += (state[index] - 1) * powers[index]; - goal += 3 * powers[index]; - } - const effects = links.map((row, plate) => { - const targets = []; - const signs = []; - let offset = 0; - for (let target = 0; target < n; target += 1) { - const sign = plate === target ? 1 : row[target]; - if (sign === 0) continue; - targets.push(target); - signs.push(sign); - offset += sign * powers[target]; - } - return { targets, signs, offset }; - }); - return { n, size, source, goal, effects }; -} - -export function decodeDigits(code, digits) { - for (let index = digits.length - 1; index >= 0; index -= 1) { - digits[index] = code % 7; - code = Math.floor(code / 7); - } -} - -export function movementLimits(digits, effect) { - let plus = 6; - let minus = 6; - for (let index = 0; index < effect.targets.length; index += 1) { - const position = digits[effect.targets[index]]; - if (effect.signs[index] === 1) { - if (6 - position < plus) plus = 6 - position; - if (position < minus) minus = position; - } else { - if (position < plus) plus = position; - if (6 - position < minus) minus = 6 - position; - } - } - return [plus, minus]; -} - -export function commandFor(plate, delta) { - return { plate: plate + 1, direction: delta > 0 ? 'left' : 'right', steps: Math.abs(delta) }; -} diff --git a/benchmarks/reference-matrix/src/solver.mjs b/benchmarks/reference-matrix/src/solver.mjs deleted file mode 100644 index b1facff..0000000 --- a/benchmarks/reference-matrix/src/solver.mjs +++ /dev/null @@ -1,25 +0,0 @@ -import { validateLockDefinition } from './lock-definition.mjs'; -import { searchBfs } from './search-bfs.mjs'; -import { normalizeSearchOptions, prepareSearch, SearchBudget } from './search-limits.mjs'; - -export function packCommand(command) { - const directionBit = command.direction === 'left' ? 0 : 1; - return (((command.plate - 1) * 2 + directionBit) * 6) + command.steps - 1; -} - -export function unpackCommand(packed) { - const steps = (packed % 6) + 1; - const plateDirection = Math.floor(packed / 6); - return { - plate: Math.floor(plateDirection / 2) + 1, - direction: plateDirection % 2 === 0 ? 'left' : 'right', - steps, - }; -} - -/** Legacy entry point: always exact BFS with its original command order. */ -export function findShortestCommands(value, options) { - const limits = normalizeSearchOptions(options); - const definition = validateLockDefinition(value); - return searchBfs(prepareSearch(definition), new SearchBudget(limits)); -} diff --git a/benchmarks/reference-matrix/src/state-codec.mjs b/benchmarks/reference-matrix/src/state-codec.mjs deleted file mode 100644 index ea0431c..0000000 --- a/benchmarks/reference-matrix/src/state-codec.mjs +++ /dev/null @@ -1,25 +0,0 @@ -export function stateSpaceSize(plateCount) { - return 7 ** plateCount; -} - -export function encodeState(state) { - let code = 0; - for (const position of state) { - code = code * 7 + (position - 1); - } - return code; -} - -export function decodeState(code, plateCount) { - const state = new Array(plateCount); - let remainder = code; - for (let index = plateCount - 1; index >= 0; index -= 1) { - state[index] = (remainder % 7) + 1; - remainder = Math.floor(remainder / 7); - } - return state; -} - -export function createTargetState(plateCount) { - return Array.from({ length: plateCount }, () => 4); -} diff --git a/benchmarks/reference-matrix/src/transition.mjs b/benchmarks/reference-matrix/src/transition.mjs deleted file mode 100644 index 6886872..0000000 --- a/benchmarks/reference-matrix/src/transition.mjs +++ /dev/null @@ -1,46 +0,0 @@ -const DIRECTIONS = ['left', 'right']; - -export function applyCommand(state, links, command) { - const sourceIndex = command.plate - 1; - const directionDelta = command.direction === 'left' ? 1 : -1; - const nextState = [...state]; - - for (let targetIndex = 0; targetIndex < state.length; targetIndex += 1) { - const coefficient = targetIndex === sourceIndex - ? 1 - : links[sourceIndex][targetIndex]; - - if (coefficient === 0) { - continue; - } - - const nextPosition = state[targetIndex] - + directionDelta * coefficient * command.steps; - if (nextPosition < 1 || nextPosition > 7) { - return null; - } - nextState[targetIndex] = nextPosition; - } - - return nextState; -} - -export function generateTransitions(state, links) { - const transitions = []; - for (let plate = 1; plate <= state.length; plate += 1) { - const sourcePosition = state[plate - 1]; - for (const direction of DIRECTIONS) { - const sourceLimit = direction === 'left' - ? 7 - sourcePosition - : sourcePosition - 1; - for (let steps = sourceLimit; steps >= 1; steps -= 1) { - const command = { plate, direction, steps }; - const nextState = applyCommand(state, links, command); - if (nextState !== null) { - transitions.push({ command, state: nextState }); - } - } - } - } - return transitions; -} diff --git a/benchmarks/snapshots/reference-matrix.json b/benchmarks/snapshots/reference-matrix.json deleted file mode 100644 index 7b9c6ed..0000000 --- a/benchmarks/snapshots/reference-matrix.json +++ /dev/null @@ -1,2394 +0,0 @@ -{ - "schemaVersion": 1, - "fixtureSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", - "source": { - "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", - "moduleSha256": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "format": "legacy" - }, - "reason": "Initial immutable matrix reference at approved migration boundary; exact A from preserved Apache BFS fixtures.", - "locks": [ - { - "id": "ruined-tower-door", - "optimumA": 17, - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - [ - 0, - -4 - ], - [ - 2, - 5 - ], - [ - 3, - -6 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 5, - -2 - ], - [ - 0, - -4 - ], - [ - 1, - 3 - ], - [ - 3, - -5 - ], - [ - 4, - -3 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 1, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "scatty-chest", - "optimumA": 9, - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - 5 - ], - [ - 4, - 5 - ], - [ - 1, - -1 - ], - [ - 3, - 5 - ], - [ - 4, - 2 - ], - [ - 0, - -2 - ], - [ - 2, - 2 - ], - [ - 4, - 6 - ], - [ - 1, - -3 - ] - ] - }, - { - "id": "cor-galom-chest", - "optimumA": 5, - "A": 5, - "U": 4, - "C": 15, - "plateSwitches": 4, - "commands": [ - [ - 3, - 4 - ], - [ - 1, - -4 - ], - [ - 0, - 4 - ], - [ - 1, - -1 - ], - [ - 2, - -2 - ] - ] - }, - { - "id": "cor-galom-bedroom-chest", - "optimumA": 14, - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - [ - 1, - -5 - ], - [ - 2, - -4 - ], - [ - 1, - -4 - ], - [ - 4, - -4 - ], - [ - 1, - -4 - ], - [ - 3, - 4 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 2, - -2 - ], - [ - 1, - -6 - ], - [ - 4, - -6 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "swamp-camp-forge-chest", - "optimumA": 11, - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 2, - 6 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 3, - -5 - ], - [ - 2, - 5 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 1, - -1 - ], - [ - 2, - 5 - ], - [ - 3, - -3 - ], - [ - 4, - -2 - ] - ] - }, - { - "id": "rice-lord-storeroom", - "optimumA": 9, - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 1 - ], - [ - 4, - 3 - ], - [ - 2, - 4 - ], - [ - 1, - 2 - ], - [ - 0, - 3 - ], - [ - 3, - 5 - ], - [ - 4, - 3 - ], - [ - 1, - 6 - ], - [ - 0, - 3 - ] - ] - }, - { - "id": "gomez-chambers-door", - "optimumA": 9, - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - [ - 3, - -3 - ], - [ - 2, - 2 - ], - [ - 1, - -2 - ], - [ - 0, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -4 - ], - [ - 5, - -4 - ], - [ - 3, - -5 - ], - [ - 1, - -3 - ] - ] - }, - { - "id": "gomez-throne-hall-chest", - "optimumA": 5, - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - [ - 3, - 6 - ], - [ - 0, - 6 - ], - [ - 1, - 2 - ], - [ - 2, - 1 - ], - [ - 3, - 1 - ] - ] - }, - { - "id": "old-camp-dungeon-door", - "optimumA": 17, - "A": 17, - "U": 6, - "C": 72, - "plateSwitches": 16, - "commands": [ - [ - 4, - -2 - ], - [ - 1, - -1 - ], - [ - 3, - 2 - ], - [ - 2, - -4 - ], - [ - 5, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - -5 - ], - [ - 5, - -6 - ], - [ - 2, - -6 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 1, - -6 - ], - [ - 0, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ], - [ - 5, - -6 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "arlin-dungeon-key", - "optimumA": 7, - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - [ - 2, - -2 - ], - [ - 5, - -2 - ], - [ - 6, - -6 - ], - [ - 0, - -3 - ], - [ - 2, - -6 - ], - [ - 1, - 3 - ], - [ - 5, - -3 - ] - ] - }, - { - "id": "gomez-wine-cellar", - "optimumA": 9, - "A": 9, - "U": 6, - "C": 41, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 2, - 5 - ], - [ - 5, - 4 - ], - [ - 3, - 4 - ], - [ - 2, - -6 - ], - [ - 4, - -5 - ], - [ - 3, - 6 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ] - ] - }, - { - "id": "gomez-bedside-chest", - "optimumA": 23, - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - [ - 2, - -1 - ], - [ - 1, - -1 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -4 - ], - [ - 0, - -4 - ], - [ - 1, - -4 - ], - [ - 4, - 2 - ], - [ - 2, - -2 - ], - [ - 3, - -5 - ], - [ - 0, - -4 - ], - [ - 1, - -6 - ], - [ - 4, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - 3 - ], - [ - 1, - -6 - ], - [ - 0, - -3 - ] - ] - }, - { - "id": "gomez-wall-chest", - "optimumA": 20, - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - [ - 0, - -2 - ], - [ - 4, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ], - [ - 5, - -6 - ], - [ - 3, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 3, - -3 - ], - [ - 5, - -5 - ], - [ - 0, - -2 - ], - [ - 5, - -2 - ], - [ - 2, - 4 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ], - [ - 1, - -3 - ] - ] - }, - { - "id": "voran-bedside-chest", - "optimumA": 9, - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 4, - -1 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 3, - 6 - ], - [ - 5, - 6 - ], - [ - 3, - 6 - ], - [ - 6, - -5 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "arto-room-door", - "optimumA": 7, - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - [ - 1, - -6 - ], - [ - 0, - -6 - ], - [ - 1, - -1 - ], - [ - 3, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - 5 - ], - [ - 2, - -3 - ] - ] - }, - { - "id": "arto-far-bed-chest", - "optimumA": 11, - "A": 11, - "U": 5, - "C": 44, - "plateSwitches": 10, - "commands": [ - [ - 1, - 4 - ], - [ - 2, - -2 - ], - [ - 5, - -2 - ], - [ - 3, - 3 - ], - [ - 4, - 6 - ], - [ - 1, - 6 - ], - [ - 5, - -6 - ], - [ - 4, - 6 - ], - [ - 1, - 3 - ], - [ - 2, - 3 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "arto-near-bed-chest", - "optimumA": 13, - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - [ - 4, - -1 - ], - [ - 0, - 6 - ], - [ - 5, - -3 - ], - [ - 3, - 4 - ], - [ - 2, - 6 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 3, - 4 - ], - [ - 0, - 6 - ], - [ - 2, - 4 - ], - [ - 5, - -6 - ], - [ - 1, - -3 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "gomez-bedrooms-door", - "optimumA": 14, - "A": 14, - "U": 6, - "C": 49, - "plateSwitches": 13, - "commands": [ - [ - 3, - -6 - ], - [ - 5, - -2 - ], - [ - 2, - 2 - ], - [ - 0, - 3 - ], - [ - 1, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - 3 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 5, - 3 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ] - ] - }, - { - "id": "gomez-bedrooms-far-chest", - "optimumA": 14, - "A": 14, - "U": 5, - "C": 43, - "plateSwitches": 13, - "commands": [ - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 3, - 2 - ], - [ - 4, - -3 - ], - [ - 2, - -4 - ], - [ - 0, - -5 - ], - [ - 1, - -4 - ], - [ - 0, - -2 - ], - [ - 3, - 2 - ], - [ - 2, - -3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "gomez-bedrooms-near-chest", - "optimumA": 12, - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - [ - 0, - 4 - ], - [ - 2, - 1 - ], - [ - 5, - -6 - ], - [ - 4, - -6 - ], - [ - 0, - 6 - ], - [ - 5, - -6 - ], - [ - 1, - -6 - ], - [ - 0, - 6 - ], - [ - 3, - -3 - ], - [ - 0, - 3 - ], - [ - 5, - -6 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "raven-tower-left-chest", - "optimumA": 11, - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - [ - 4, - 2 - ], - [ - 5, - -3 - ], - [ - 3, - 2 - ], - [ - 0, - 5 - ], - [ - 2, - -4 - ], - [ - 4, - 5 - ], - [ - 5, - -1 - ], - [ - 1, - -3 - ], - [ - 3, - 5 - ], - [ - 0, - 3 - ], - [ - 1, - -2 - ] - ] - }, - { - "id": "raven-tower-right-chest", - "optimumA": 8, - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - [ - 1, - 5 - ], - [ - 4, - 1 - ], - [ - 5, - -6 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -2 - ], - [ - 3, - 2 - ], - [ - 5, - -1 - ] - ] - }, - { - "id": "raven-tower-floor2-stairs-chest", - "optimumA": 16, - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - [ - 5, - 3 - ], - [ - 0, - -1 - ], - [ - 4, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - 4 - ], - [ - 3, - -2 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 1, - 6 - ], - [ - 2, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "raven-tower-floor2-chest-2", - "optimumA": 8, - "A": 8, - "U": 5, - "C": 29, - "plateSwitches": 7, - "commands": [ - [ - 3, - 1 - ], - [ - 2, - -5 - ], - [ - 4, - 5 - ], - [ - 3, - -3 - ], - [ - 0, - -1 - ], - [ - 1, - 5 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ] - ] - }, - { - "id": "raven-tower-floor3-entrance-chest", - "optimumA": 14, - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - [ - 3, - -4 - ], - [ - 1, - 1 - ], - [ - 4, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - 2 - ], - [ - 4, - -4 - ], - [ - 2, - -4 - ], - [ - 1, - 3 - ], - [ - 3, - -6 - ], - [ - 4, - -3 - ], - [ - 0, - -3 - ], - [ - 1, - 3 - ], - [ - 4, - -5 - ], - [ - 0, - -2 - ] - ] - }, - { - "id": "raven-tower-floor3-empty-chest", - "optimumA": 9, - "A": 9, - "U": 5, - "C": 37, - "plateSwitches": 8, - "commands": [ - [ - 0, - -2 - ], - [ - 4, - 6 - ], - [ - 2, - 3 - ], - [ - 1, - -3 - ], - [ - 4, - 6 - ], - [ - 3, - -4 - ], - [ - 0, - -6 - ], - [ - 4, - 4 - ], - [ - 2, - 3 - ] - ] - }, - { - "id": "raven-tower-floor4-elixirs-chest", - "optimumA": 8, - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - [ - 1, - -5 - ], - [ - 2, - -5 - ], - [ - 1, - -5 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ], - [ - 2, - -6 - ], - [ - 1, - -3 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "raven-tower-floor4-food-chest", - "optimumA": 11, - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - [ - 2, - 4 - ], - [ - 0, - 4 - ], - [ - 2, - 4 - ], - [ - 4, - 6 - ], - [ - 0, - 5 - ], - [ - 3, - -1 - ], - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - 2 - ], - [ - 5, - -3 - ] - ] - }, - { - "id": "raven-tower-top-chest", - "optimumA": 20, - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - [ - 1, - -3 - ], - [ - 3, - 1 - ], - [ - 0, - -1 - ], - [ - 4, - -2 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 3, - 2 - ], - [ - 5, - -4 - ], - [ - 2, - -1 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - -6 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "fire-lizard-cave-chest", - "optimumA": 10, - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - [ - 0, - -3 - ], - [ - 2, - -5 - ], - [ - 4, - 4 - ], - [ - 1, - 3 - ], - [ - 5, - -5 - ], - [ - 0, - -6 - ], - [ - 3, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - 6 - ], - [ - 0, - -3 - ] - ] - }, - { - "id": "silas-tavern-basement-chest", - "optimumA": 9, - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 2 - ], - [ - 4, - 2 - ], - [ - 2, - 4 - ], - [ - 3, - -3 - ], - [ - 1, - -4 - ], - [ - 3, - -3 - ], - [ - 4, - 6 - ], - [ - 2, - 3 - ], - [ - 3, - -3 - ] - ] - }, - { - "id": "silas-tavern-storeroom-chest", - "optimumA": 9, - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - -3 - ], - [ - 3, - 4 - ], - [ - 5, - -6 - ], - [ - 0, - 2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -6 - ], - [ - 3, - 3 - ], - [ - 5, - -3 - ] - ] - }, - { - "id": "silas-tavern-storeroom-door", - "optimumA": 10, - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - [ - 3, - 2 - ], - [ - 5, - -3 - ], - [ - 1, - -3 - ], - [ - 5, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - 3 - ], - [ - 5, - -3 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 2, - -3 - ] - ] - }, - { - "id": "jan-pond-house-chest", - "optimumA": 8, - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - [ - 0, - -4 - ], - [ - 2, - -6 - ], - [ - 3, - 4 - ], - [ - 4, - 3 - ], - [ - 5, - -3 - ], - [ - 3, - 5 - ], - [ - 5, - -5 - ], - [ - 1, - 3 - ] - ] - }, - { - "id": "graham-chest", - "optimumA": 10, - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - [ - 1, - 5 - ], - [ - 4, - 6 - ], - [ - 0, - -5 - ], - [ - 1, - 6 - ], - [ - 4, - 5 - ], - [ - 1, - 5 - ], - [ - 2, - 2 - ], - [ - 3, - -3 - ], - [ - 4, - 2 - ], - [ - 5, - -1 - ] - ] - }, - { - "id": "gravo-chest", - "optimumA": 8, - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - [ - 0, - -5 - ], - [ - 4, - -1 - ], - [ - 1, - 3 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 3, - 3 - ], - [ - 4, - -6 - ], - [ - 1, - 3 - ] - ] - }, - { - "id": "diego-chest", - "optimumA": 11, - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - [ - 0, - 2 - ], - [ - 1, - 1 - ], - [ - 2, - -6 - ], - [ - 3, - -1 - ], - [ - 1, - 3 - ], - [ - 4, - 3 - ], - [ - 2, - -6 - ], - [ - 5, - 3 - ], - [ - 1, - 6 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ] - ] - }, - { - "id": "grim-chest", - "optimumA": 8, - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - [ - 4, - -2 - ], - [ - 3, - 1 - ], - [ - 1, - 6 - ], - [ - 2, - -1 - ], - [ - 0, - -5 - ], - [ - 3, - 4 - ], - [ - 0, - -1 - ], - [ - 5, - -1 - ] - ] - }, - { - "id": "mercenary-near-grim-chest", - "optimumA": 8, - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - [ - 2, - 3 - ], - [ - 1, - 3 - ], - [ - 3, - -6 - ], - [ - 5, - 6 - ], - [ - 0, - -2 - ], - [ - 3, - -2 - ], - [ - 4, - 2 - ], - [ - 5, - 1 - ] - ] - }, - { - "id": "popular-lock", - "optimumA": 13, - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - [ - 4, - -4 - ], - [ - 2, - -1 - ], - [ - 3, - -2 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - 6 - ], - [ - 2, - -3 - ], - [ - 4, - -6 - ], - [ - 1, - -3 - ], - [ - 0, - 3 - ], - [ - 3, - -3 - ], - [ - 4, - -6 - ], - [ - 0, - 3 - ] - ] - }, - { - "id": "alberto-mine-hut-chest", - "optimumA": 9, - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - [ - 3, - -4 - ], - [ - 1, - -2 - ], - [ - 4, - 6 - ], - [ - 0, - -3 - ], - [ - 3, - -6 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - 3 - ] - ] - }, - { - "id": "alberto-mine-upstairs-left-chest", - "optimumA": 4, - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 1, - 6 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "alberto-mine-upstairs-right-chest", - "optimumA": 5, - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - [ - 0, - 3 - ], - [ - 1, - 2 - ], - [ - 2, - 2 - ], - [ - 4, - 4 - ], - [ - 3, - 2 - ] - ] - }, - { - "id": "cavalorn-storeroom-door", - "optimumA": 11, - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 3, - -2 - ], - [ - 5, - 4 - ], - [ - 0, - -1 - ], - [ - 1, - -6 - ], - [ - 0, - -4 - ], - [ - 3, - -2 - ], - [ - 1, - -6 - ], - [ - 4, - -4 - ], - [ - 5, - 6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ] - ] - }, - { - "id": "old-to-new-camp-bandits-chest", - "optimumA": 10, - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - [ - 4, - 6 - ], - [ - 5, - -2 - ], - [ - 2, - -1 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ] - ] - } - ] -} diff --git a/benchmarks/solver.bench.ts b/benchmarks/solver.bench.ts deleted file mode 100644 index 390add8..0000000 --- a/benchmarks/solver.bench.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { test } from 'vitest'; -import { loadCatalog } from './catalog.ts'; -import { loadImplementation } from './harness.ts'; -import { replayCommands } from './validation.ts'; -import { fileURLToPath } from 'node:url'; - -const candidate = await loadImplementation(fileURLToPath(new URL('../src/index.ts', import.meta.url)), 'tuple', 'Developer source'); -const baseline = await loadImplementation(fileURLToPath(new URL('./reference-matrix/src/index.mjs', import.meta.url)), 'legacy', 'Pinned matrix'); -const locks = loadCatalog(); -// One representative from each encountered plate count, selected deterministically from the fixed catalog. -const representatives = [...new Map(locks.map((lock) => [lock.definition.state.length, lock])).values()]; -for (const lock of representatives) { - for (const solver of [candidate, baseline]) { - const commands = solver.normalize(solver.solve(structuredClone(lock.definition))); - if (commands === null || replayCommands(lock.definition, commands).some((position) => position !== 4)) throw new Error('Developer benchmark setup did not reach target.'); - } - test(`${lock.id} (${lock.definition.state.length} plates)`, async ({ bench }) => { - await bench.compare( - bench('candidate tuple API', () => { candidate.solve(lock.definition); }), - bench('pinned legacy matrix API', () => { baseline.solve(lock.definition); }), - { iterations: 10, time: 100 }, - ); - }); -} diff --git a/cli/main.ts b/cli/main.ts deleted file mode 100644 index 17719ad..0000000 --- a/cli/main.ts +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env node -import { runCli } from './cli.ts'; - -process.exitCode = await runCli(process.argv.slice(2)); diff --git a/docs/benchmarks/README.md b/docs/benchmarks/README.md deleted file mode 100644 index 9b92765..0000000 --- a/docs/benchmarks/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# Проверенный запуск на 45 замках - -Полный отчёт: [Markdown](run-20260907T070258001Z-f78dff13/benchmark.md) -и [JSON с отдельными замерами и путями](run-20260907T070258001Z-f78dff13/benchmark.json). - -Проверен commit `6b1cfbc13bcec68f609d45d5dc97504dd84f1760` при чистом рабочем дереве. -Сторонний решатель: UnlockMyLoot `eee0bf50ebcb7fffd2b47954fd76bb015854e365`. -Все 45 конфигураций прошли проверки корректности на каждом прогреве и повторе. -Общая тестовая проверка нашего проекта: **113/113**, без пропусков. - -## Результат - -| Реализация | Действия A, сумма | Разные пластины U, сумма | Сдвиги C, сумма | Медиана времени по замкам, мс | Сумма медиан по замкам, мс | -|---|---:|---:|---:|---:|---:| -| Новый матричный A* | 483 | 245 | 1723 | 0,196 | 49,631 | -| Текущий точный BFS | 483 | 245 | 1727 | 11,221 | 423,231 | -| Исходный BFS нашего проекта | 483 | 245 | 1727 | 67,716 | 2537,917 | -| UnlockMyLoot | 491 | 245 | 1695 | 26,897 | 1465,337 | - -В столбце «Медиана времени по замкам» взята медиана 45 индивидуальных медиан, -каждая из которых получена из пяти повторов после одного прогрева. -Сумма медиан не является медианой времени полного прохода каталога. - -По числу действий относительно UnlockMyLoot: **5 выигрышей, 40 равенств, -0 проигрышей**. Разных выбранных пластин столько же на каждом из 45 входов. -Сдвигов больше на 6 входах, столько же на 39. Новый поиск быстрее стороннего -по индивидуальной медиане на всех 45 входах в этом запуске; максимум индивидуальных -медиан нового поиска — 7,302 мс. Это наблюдение, не универсальная гарантия скорости. - -| Замок | UnlockMyLoot A → новый A | UnlockMyLoot C → новый C | -|---|---:|---:| -| old-camp-dungeon-door | 18 → 17 | 68 → 72 | -| gomez-wine-cellar | 11 → 9 | 31 → 41 | -| arto-far-bed-chest | 13 → 11 | 40 → 44 | -| gomez-bedrooms-far-chest | 16 → 14 | 39 → 43 | -| raven-tower-floor2-chest-2 | 9 → 8 | 27 → 29 | - -Исходный BFS нашего проекта уже находил минимум действий на этих входах. -Матричный поиск ускоряет получение этого минимума. Разница с UnlockMyLoot связана -с другим приоритетом: его текущий режим сначала минимизирует сдвиги, наш — действия. -Глобальный минимум U и C среди равных по A решений не заявляется. - -## Воспроизведение - -Требуется Node.js 20+. Сторонний checkout хранится отдельно; его solver не включён -в наш репозиторий. - -```bash -git clone https://github.com/Marcus-Rise/gothic-lock-solver.git -git -C gothic-lock-solver checkout 6b1cfbc13bcec68f609d45d5dc97504dd84f1760 -git clone https://github.com/1h8s/unlockmyloot.git -git -C unlockmyloot checkout eee0bf50ebcb7fffd2b47954fd76bb015854e365 -cd gothic-lock-solver -node --test -node scripts/build-browser.mjs --check -node scripts/benchmark.mjs --reference ../unlockmyloot --repetitions 5 --warmups 1 --output-dir docs/benchmarks -``` - -Отчёт содержит версии Node/V8, CPU/ОС, хэши входов и исходников, порядок измерений, -границы таймера и все отдельные замеры. Все реализации исполнялись в одном -Node.js runtime; проверенная функция UnlockMyLoot компилировалась без изменений -в том же JS-контексте. Проверка путей, копирование входов и нормализация ответов -исключены из измерения. GC естественный, без принудительного запуска. - -## Границы проверки - -Проверки подтвердили независимое воспроизведение путей, минимум действий через -точный BFS, детерминированность, отсутствие изменения входов и сохранение исходного -порядка BFS. Математическое ядро и сценарий измерений прошли независимый review -без существенных замечаний. - -Времена относятся к Node.js на указанной машине. Они не измеряют браузер, Worker, -телефон или время действий игрока. Изолированные проверки ESM и готового classic -script прошли без Node/DOM-зависимостей. Попытка проверки HTML в настоящем браузере -не смогла загрузить локальный сервер среды (`net::ERR_BLOCKED_BY_CLIENT`), поэтому -реальный браузерный запуск и Worker остаются отдельной ручной проверкой. diff --git a/docs/benchmarks/run-20260907T070258001Z-f78dff13/benchmark.json b/docs/benchmarks/run-20260907T070258001Z-f78dff13/benchmark.json deleted file mode 100644 index 4093e15..0000000 --- a/docs/benchmarks/run-20260907T070258001Z-f78dff13/benchmark.json +++ /dev/null @@ -1,17137 +0,0 @@ -{ - "schemaVersion": 1, - "createdAt": "2026-09-07T07:02:58.001Z", - "config": { - "repetitions": 5, - "warmups": 1, - "lockCount": 45, - "scope": "full-45-catalog", - "inputSha256": "c75f6378543138d974b11e9af9d5959f21ed6dbe58d71ecb12a11b197fe5bb09", - "units": "milliseconds", - "executionOrder": "Per lock, warmup and measured rounds rotate solver order deterministically.", - "timingBoundary": "Only the synchronous solver call is timed. Input cloning, reference click grouping, replay, metrics, and reporting are outside timing. Current and baseline calls include their public facade validation/result construction.", - "gc": "Natural Node.js garbage collection; no forced GC." - }, - "metadata": { - "environment": { - "node": "v24.19.0", - "v8": "13.6.233.17-node.51", - "platform": "linux", - "arch": "x64", - "osRelease": "6.18.35", - "cpu": "AMD EPYC 9V74 80-Core Processor", - "logicalCpus": 9, - "totalMemoryBytes": 23109910528, - "execArgv": [] - }, - "source": { - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", - "dirty": false, - "core": { - "sha256": "064dd862f17ea1a8fdd00abd4e06dfb024ffc4dcf98cb648c881a163c4599079", - "files": { - "cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", - "index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", - "matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", - "matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", - "result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", - "search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", - "solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", - "state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" - } - }, - "harness": { - "sha256": "becf8b6ca5b15dd64d91e52939bb1bf8b46816f4f62e70b27629e17c26f9bde4", - "files": { - "baseline/src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", - "baseline/src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", - "baseline/src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "baseline/src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", - "baseline/src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "baseline/src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", - "catalog.mjs": "1aeead30de3b4041309df18edae2daa206212cf6bb5e8b7031e77743d450a497", - "harness.mjs": "49c73bd6cd5b096f079446ca89a272b73f63e333ac187f814e126d66198ed2a0", - "validation.mjs": "3162b5374be266e5963e63a494006666c2b29ff2be10f2d2b58c8c57e036759e" - }, - "cliSha256": "57276d8b7dfbc48c8348d021b443f64d14c3bb3dbce753db8d54d51ee2fbf76e" - } - }, - "fixtures": { - "count": 45, - "catalogSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", - "manifestSha256": "a634d5328fd8d41470397b622ae0bd32b8aac0355dfc31975e023a63a62e65c9", - "repository": "https://github.com/1h8s/unlockmyloot", - "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365" - }, - "baseline": { - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "a87e739a22ccc4215d6d9b14fa06b0738171cb60", - "license": "Apache-2.0", - "files": { - "src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", - "src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", - "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", - "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", - "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" - }, - "notes": "Unmodified source snapshots; imports remain relative within this directory. No upstream UnlockMyLoot solver code is included." - }, - "reference": { - "measured": true, - "repository": "https://github.com/1h8s/unlockmyloot", - "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "path": "index.html", - "fileSha256": "691f744099a9169f30e9883fddc5fdbb4949d306f3011e601678419d7b93821b", - "solverBlockSha256": "d5bc85f40fd7c5b61097403df399ecb7dfd2b58924dc7a670aa0d05306017380", - "execution": "Unchanged source compiled with Function in the same Node.js realm; no browser or vm context." - } - }, - "implementations": { - "matrix-astar": { - "label": "Matrix A*", - "requiresOptimality": true - }, - "bfs": { - "label": "Current exact BFS", - "requiresOptimality": true - }, - "baseline": { - "label": "Original Apache BFS", - "requiresOptimality": true - }, - "reference": { - "label": "UnlockMyLoot (live pinned reference)", - "requiresOptimality": false - } - }, - "correctness": { - "passed": true, - "lockCount": 45, - "checks": [ - "Every warmup and measured solution independently replayed one division at a time.", - "Matrix A*, exact BFS, and original Apache BFS equal the stored exact action minimum.", - "Current BFS preserves the pinned original BFS command sequence.", - "Inputs are unchanged and every implementation returns a deterministic path.", - "Live reference path and A/U/C equal the cached pinned observation." - ] - }, - "aggregates": { - "matrix-astar": { - "A": 483, - "U": 245, - "C": 1723, - "sumOfMediansMs": 49.63095100000089 - }, - "bfs": { - "A": 483, - "U": 245, - "C": 1727, - "sumOfMediansMs": 423.23053500001197 - }, - "baseline": { - "A": 483, - "U": 245, - "C": 1727, - "sumOfMediansMs": 2537.9171760000077 - }, - "reference": { - "A": 491, - "U": 245, - "C": 1695, - "sumOfMediansMs": 1465.3368119999984 - } - }, - "comparisons": { - "bfs": { - "actions": { - "wins": 0, - "ties": 45, - "losses": 0 - }, - "uniquePlates": { - "wins": 0, - "ties": 45, - "losses": 0 - }, - "clicks": { - "wins": 2, - "ties": 42, - "losses": 1 - }, - "timingCompared": true, - "timing": { - "wins": 43, - "ties": 0, - "losses": 2, - "geometricMeanSpeedup": 16.23201179805268, - "ratioOfSummedMedians": 8.527552393666694 - } - }, - "baseline": { - "actions": { - "wins": 0, - "ties": 45, - "losses": 0 - }, - "uniquePlates": { - "wins": 0, - "ties": 45, - "losses": 0 - }, - "clicks": { - "wins": 2, - "ties": 42, - "losses": 1 - }, - "timingCompared": true, - "timing": { - "wins": 45, - "ties": 0, - "losses": 0, - "geometricMeanSpeedup": 88.39310296954534, - "ratioOfSummedMedians": 51.13577565741108 - } - }, - "reference": { - "actions": { - "wins": 5, - "ties": 40, - "losses": 0 - }, - "uniquePlates": { - "wins": 0, - "ties": 45, - "losses": 0 - }, - "clicks": { - "wins": 0, - "ties": 39, - "losses": 6 - }, - "timingCompared": true, - "timing": { - "wins": 45, - "ties": 0, - "losses": 0, - "geometricMeanSpeedup": 52.86312829307665, - "ratioOfSummedMedians": 29.524657143885317 - } - }, - "historicalReference": { - "actions": { - "wins": 5, - "ties": 40, - "losses": 0 - }, - "uniquePlates": { - "wins": 0, - "ties": 45, - "losses": 0 - }, - "clicks": { - "wins": 0, - "ties": 39, - "losses": 6 - }, - "timingCompared": false - } - }, - "results": [ - { - "id": "ruined-tower-door", - "title": "Дверь Разрушенной башни", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/ruined-tower-door/", - "code": "aUkAACUBAAJikA", - "expectedActions": 17, - "solvers": { - "matrix-astar": { - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 3.45307600000001, - "median": 4.894904999999994, - "max": 9.461225000000013 - }, - "samplesMs": [ - 5.811674000000039, - 4.894904999999994, - 9.461225000000013, - 3.527486999999951, - 3.45307600000001 - ] - }, - "bfs": { - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 13.383325999999897, - "median": 15.879230000000007, - "max": 25.095195999999987 - }, - "samplesMs": [ - 25.095195999999987, - 15.879230000000007, - 17.286757999999963, - 15.852350000000001, - 13.383325999999897 - ] - }, - "baseline": { - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 77.48588100000006, - "median": 89.62647500000003, - "max": 108.14718999999991 - }, - "samplesMs": [ - 98.286856, - 108.14718999999991, - 89.62647500000003, - 87.04519200000004, - 77.48588100000006 - ] - }, - "reference": { - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 79.48287400000004, - "median": 85.13851399999999, - "max": 124.03954099999999 - }, - "samplesMs": [ - 94.57184599999994, - 79.48287400000004, - 124.03954099999999, - 83.54486599999996, - 85.13851399999999 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 91.817025, - "median": 96.49636299999997, - "max": 126.86948000000007 - } - } - }, - { - "id": "scatty-chest", - "title": "Сундук Скатти", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/scatty-chest/", - "code": "SojAgCBDQA", - "expectedActions": 9, - "solvers": { - "matrix-astar": { - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.1425249999999778, - "median": 0.19603500000016538, - "max": 0.24954500000012558 - }, - "samplesMs": [ - 0.24954500000012558, - 0.22157300000003488, - 0.19603500000016538, - 0.1425249999999778, - 0.15903899999989335 - ] - }, - "bfs": { - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 2.27847799999995, - "median": 2.334081999999853, - "max": 3.2826689999999417 - }, - "samplesMs": [ - 3.193435000000136, - 3.2826689999999417, - 2.27847799999995, - 2.334081999999853, - 2.299539999999979 - ] - }, - "baseline": { - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 11.29907900000012, - "median": 11.719831999999997, - "max": 14.03815099999997 - }, - "samplesMs": [ - 14.03815099999997, - 12.689040999999861, - 11.719831999999997, - 11.29907900000012, - 11.465169999999944 - ] - }, - "reference": { - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 5.317440000000033, - "median": 8.441120000000183, - "max": 15.322402999999895 - }, - "samplesMs": [ - 8.441120000000183, - 8.47560199999998, - 5.317440000000033, - 5.322917999999845, - 15.322402999999895 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 9.513588000000027, - "median": 9.766299000000117, - "max": 12.794235999999955 - } - } - }, - { - "id": "cor-galom-chest", - "title": "Сундук Кор Галома", - "plateCount": 4, - "url": "https://unlockmyloot.com/locks/cor-galom-chest/", - "code": "LagFFgA", - "expectedActions": 5, - "solvers": { - "matrix-astar": { - "A": 5, - "U": 4, - "C": 15, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 0.04282399999988229, - "median": 0.05560399999990295, - "max": 0.08443699999997989 - }, - "samplesMs": [ - 0.05560399999990295, - 0.08443699999997989, - 0.058818999999857624, - 0.04584899999986192, - 0.04282399999988229 - ] - }, - "bfs": { - "A": 5, - "U": 4, - "C": 19, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 0.12644000000000233, - "median": 0.12936400000012327, - "max": 0.1602109999998902 - }, - "samplesMs": [ - 0.1288839999999709, - 0.12968500000010863, - 0.1602109999998902, - 0.12936400000012327, - 0.12644000000000233 - ] - }, - "baseline": { - "A": 5, - "U": 4, - "C": 19, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 0.45256099999983235, - "median": 0.4729010000000926, - "max": 0.49103900000000067 - }, - "samplesMs": [ - 0.49103900000000067, - 0.47248100000001614, - 0.4788700000001427, - 0.4729010000000926, - 0.45256099999983235 - ] - }, - "reference": { - "A": 5, - "U": 4, - "C": 15, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - } - ], - "timingMs": { - "min": 0.30269499999985783, - "median": 0.32210399999985384, - "max": 0.38023199999997814 - }, - "samplesMs": [ - 0.38023199999997814, - 0.32210399999985384, - 0.31444299999998293, - 0.30269499999985783, - 0.33010699999999815 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 5, - "U": 4, - "C": 15, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - } - ], - "historicalTimingMs": { - "min": 0.3375069999999596, - "median": 0.507082999999966, - "max": 0.6312800000000607 - } - } - }, - { - "id": "cor-galom-bedroom-chest", - "title": "Сундук в спальне Кор Галома", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/cor-galom-bedroom-chest/", - "code": "a2YMQAAIiECEAA", - "expectedActions": 14, - "solvers": { - "matrix-astar": { - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.246550999999954, - "median": 0.32631099999980506, - "max": 0.40945699999997487 - }, - "samplesMs": [ - 0.3410030000000006, - 0.246550999999954, - 0.40945699999997487, - 0.2560849999999846, - 0.32631099999980506 - ] - }, - "bfs": { - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 12.28265999999985, - "median": 12.730002999999897, - "max": 13.442364999999882 - }, - "samplesMs": [ - 12.721741000000065, - 12.730002999999897, - 13.442364999999882, - 13.175422000000026, - 12.28265999999985 - ] - }, - "baseline": { - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 70.64990499999976, - "median": 72.50764000000004, - "max": 74.4288610000001 - }, - "samplesMs": [ - 74.4288610000001, - 73.53681900000015, - 71.80593399999998, - 72.50764000000004, - 70.64990499999976 - ] - }, - "reference": { - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 29.965103999999883, - "median": 30.333096999999952, - "max": 38.59786200000008 - }, - "samplesMs": [ - 33.34818300000006, - 29.965103999999883, - 30.333096999999952, - 38.59786200000008, - 30.23817500000041 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 30.516717999999855, - "median": 31.42274099999986, - "max": 34.788095999999996 - } - } - }, - { - "id": "swamp-camp-forge-chest", - "title": "Сундук в кузнице Болотного лагеря", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/swamp-camp-forge-chest/", - "code": "YQY0gBgAAGACIE", - "expectedActions": 11, - "solvers": { - "matrix-astar": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 0.2597609999997985, - "median": 0.2701670000001286, - "max": 0.3253899999999703 - }, - "samplesMs": [ - 0.2597609999997985, - 0.2701670000001286, - 0.3253899999999703, - 0.26081299999987095, - 0.30051200000025347 - ] - }, - "bfs": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 17.623634999999922, - "median": 17.761071999999785, - "max": 18.015034000000014 - }, - "samplesMs": [ - 17.761071999999785, - 18.015034000000014, - 17.84554900000012, - 17.751026999999794, - 17.623634999999922 - ] - }, - "baseline": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 105.26430400000027, - "median": 107.644315, - "max": 113.35371499999974 - }, - "samplesMs": [ - 105.26430400000027, - 107.644315, - 113.35371499999974, - 107.33653100000038, - 108.5920799999999 - ] - }, - "reference": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 35.30947400000014, - "median": 36.690352000000075, - "max": 40.80572399999983 - }, - "samplesMs": [ - 40.80572399999983, - 36.690352000000075, - 36.14560299999994, - 39.86313500000006, - 35.30947400000014 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 36.47057800000039, - "median": 37.77093500000001, - "max": 38.26945599999999 - } - } - }, - { - "id": "rice-lord-storeroom", - "title": "Склад Рисового лорда (3-й этаж)", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/rice-lord-storeroom/", - "code": "RY2VUBQAiA", - "expectedActions": 9, - "solvers": { - "matrix-astar": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.13477199999988443, - "median": 0.16157299999986208, - "max": 0.17313999999987573 - }, - "samplesMs": [ - 0.16157299999986208, - 0.13477199999988443, - 0.17313999999987573, - 0.1627150000003894, - 0.15835799999968003 - ] - }, - "bfs": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 1.684364000000187, - "median": 1.6987959999996747, - "max": 1.9495330000004287 - }, - "samplesMs": [ - 1.684364000000187, - 1.7642550000000483, - 1.6882299999997485, - 1.9495330000004287, - 1.6987959999996747 - ] - }, - "baseline": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 8.109771999999793, - "median": 8.252085999999963, - "max": 8.363103000000137 - }, - "samplesMs": [ - 8.363103000000137, - 8.211424000000079, - 8.109771999999793, - 8.279035999999905, - 8.252085999999963 - ] - }, - "reference": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 4.524296999999933, - "median": 4.608883999999762, - "max": 4.867021999999906 - }, - "samplesMs": [ - 4.68925500000023, - 4.524296999999933, - 4.867021999999906, - 4.535954999999831, - 4.608883999999762 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 4.488335000000006, - "median": 4.637408999999934, - "max": 4.859664000000066 - } - } - }, - { - "id": "gomez-chambers-door", - "title": "Дверь в покои Гомеза", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/gomez-chambers-door/", - "code": "YiNAIJpEEAiAAA", - "expectedActions": 9, - "solvers": { - "matrix-astar": { - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.21509299999979703, - "median": 0.2234960000000683, - "max": 0.40059300000029907 - }, - "samplesMs": [ - 0.2234960000000683, - 0.40059300000029907, - 0.28184399999963716, - 0.21509299999979703, - 0.22211400000014692 - ] - }, - "bfs": { - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 13.604608000000098, - "median": 15.005065000000286, - "max": 17.20152999999982 - }, - "samplesMs": [ - 14.522829999999885, - 17.20152999999982, - 15.005065000000286, - 13.604608000000098, - 15.536575000000084 - ] - }, - "baseline": { - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 75.37116799999967, - "median": 80.54447200000004, - "max": 83.78035 - }, - "samplesMs": [ - 79.58682099999987, - 82.10630200000014, - 83.78035, - 80.54447200000004, - 75.37116799999967 - ] - }, - "reference": { - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 28.923345999999583, - "median": 30.335059999999885, - "max": 35.09097500000007 - }, - "samplesMs": [ - 29.580093999999917, - 30.335059999999885, - 35.09097500000007, - 31.36678299999994, - 28.923345999999583 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 29.93992900000012, - "median": 30.68829599999981, - "max": 31.623243999999886 - } - } - }, - { - "id": "gomez-throne-hall-chest", - "title": "Сундук в тронном зале у Гомеза", - "plateCount": 4, - "url": "https://unlockmyloot.com/locks/gomez-throne-hall-chest/", - "code": "IIAKWgA", - "expectedActions": 5, - "solvers": { - "matrix-astar": { - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - } - ], - "timingMs": { - "min": 0.0261000000000422, - "median": 0.03207799999972849, - "max": 0.03897800000004281 - }, - "samplesMs": [ - 0.03560400000014852, - 0.03897800000004281, - 0.03207799999972849, - 0.0261000000000422, - 0.028272000000015396 - ] - }, - "bfs": { - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - } - ], - "timingMs": { - "min": 0.048131999999895925, - "median": 0.05973999999969237, - "max": 0.06918399999995017 - }, - "samplesMs": [ - 0.061361999999917316, - 0.05973999999969237, - 0.05593399999997928, - 0.048131999999895925, - 0.06918399999995017 - ] - }, - "baseline": { - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - } - ], - "timingMs": { - "min": 0.1722789999998895, - "median": 0.1746619999998984, - "max": 0.18212399999993067 - }, - "samplesMs": [ - 0.18212399999993067, - 0.1810219999997571, - 0.1746619999998984, - 0.17381199999999808, - 0.1722789999998895 - ] - }, - "reference": { - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 0.21751700000004348, - "median": 0.23293100000000777, - "max": 0.25035699999989447 - }, - "samplesMs": [ - 0.2355040000002191, - 0.23293100000000777, - 0.25035699999989447, - 0.21751700000004348, - 0.22672099999999773 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 0.25226000000020576, - "median": 0.3677139999999781, - "max": 0.39675699999952485 - } - } - }, - { - "id": "old-camp-dungeon-door", - "title": "Дверь в темнице с расстрелянным призраком", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/old-camp-dungeon-door/", - "code": "YMTCAKIgIEBiAA", - "expectedActions": 17, - "solvers": { - "matrix-astar": { - "A": 17, - "U": 6, - "C": 72, - "plateSwitches": 16, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 2.305848999999398, - "median": 2.3744320000005246, - "max": 2.994915999999648 - }, - "samplesMs": [ - 2.305848999999398, - 2.994915999999648, - 2.3744320000005246, - 2.3572570000005726, - 2.465860000000248 - ] - }, - "bfs": { - "A": 17, - "U": 6, - "C": 72, - "plateSwitches": 16, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 14.948962000000392, - "median": 15.437987000000248, - "max": 19.826901000000362 - }, - "samplesMs": [ - 15.437987000000248, - 14.948962000000392, - 15.152798000000075, - 19.826901000000362, - 15.456804999999804 - ] - }, - "baseline": { - "A": 17, - "U": 6, - "C": 72, - "plateSwitches": 16, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 82.73822199999995, - "median": 85.22048600000016, - "max": 92.24529600000005 - }, - "samplesMs": [ - 82.73822199999995, - 92.24529600000005, - 85.08997, - 87.70465400000012, - 85.22048600000016 - ] - }, - "reference": { - "A": 18, - "U": 6, - "C": 68, - "plateSwitches": 17, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 39.82016099999964, - "median": 40.37351299999955, - "max": 44.69285200000013 - }, - "samplesMs": [ - 40.37351299999955, - 44.69285200000013, - 39.82016099999964, - 40.002465000000484, - 40.941437000000406 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 18, - "U": 6, - "C": 68, - "plateSwitches": 17, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 41.57797500000015, - "median": 42.28448800000024, - "max": 46.73487599999953 - } - } - }, - { - "id": "arlin-dungeon-key", - "title": "Ключ от темницы Арлина", - "plateCount": 7, - "url": "https://unlockmyloot.com/locks/arlin-dungeon-key/", - "code": "gBDXAECQhAAQAQAIRAA", - "expectedActions": 7, - "solvers": { - "matrix-astar": { - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 7, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 7.17545599999994, - "median": 7.3016549999993, - "max": 8.008499000000484 - }, - "samplesMs": [ - 8.008499000000484, - 7.395747000000483, - 7.279462999999851, - 7.17545599999994, - 7.3016549999993 - ] - }, - "bfs": { - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 7, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 7.162446000000273, - "median": 7.25756999999976, - "max": 7.964862999999241 - }, - "samplesMs": [ - 7.964862999999241, - 7.25756999999976, - 7.162446000000273, - 7.221013999999741, - 7.285120000000461 - ] - }, - "baseline": { - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 7, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 43.22346199999993, - "median": 44.592169999999896, - "max": 48.96067200000016 - }, - "samplesMs": [ - 48.96067200000016, - 44.592169999999896, - 43.40488499999992, - 43.22346199999993, - 47.93774300000041 - ] - }, - "reference": { - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 7, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 20.249174999999923, - "median": 20.278919999999744, - "max": 24.130545999999413 - }, - "samplesMs": [ - 21.914961000000403, - 24.130545999999413, - 20.249174999999923, - 20.278919999999744, - 20.275265000000218 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 7, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 22.995531999999912, - "median": 23.810668999999507, - "max": 25.53016800000023 - } - } - }, - { - "id": "gomez-wine-cellar", - "title": "Винный погреб Гомеза", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/gomez-wine-cellar/", - "code": "bI02qgAAIBgEAg", - "expectedActions": 9, - "solvers": { - "matrix-astar": { - "A": 9, - "U": 6, - "C": 41, - "plateSwitches": 8, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.12915400000019872, - "median": 0.1397610000003624, - "max": 0.2738119999994524 - }, - "samplesMs": [ - 0.2738119999994524, - 0.1988389999996798, - 0.13804799999979878, - 0.1397610000003624, - 0.12915400000019872 - ] - }, - "bfs": { - "A": 9, - "U": 6, - "C": 41, - "plateSwitches": 8, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 8.284353999999439, - "median": 8.696474000000308, - "max": 9.232170999999653 - }, - "samplesMs": [ - 8.284353999999439, - 8.696474000000308, - 9.145520000000033, - 8.663674999999785, - 9.232170999999653 - ] - }, - "baseline": { - "A": 9, - "U": 6, - "C": 41, - "plateSwitches": 8, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 53.11341999999968, - "median": 53.189674999999625, - "max": 56.717633000000205 - }, - "samplesMs": [ - 53.11341999999968, - 53.458159000000705, - 53.189674999999625, - 53.183266, - 56.717633000000205 - ] - }, - "reference": { - "A": 11, - "U": 6, - "C": 31, - "plateSwitches": 10, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 26.219829000000573, - "median": 26.63177799999994, - "max": 34.84491499999967 - }, - "samplesMs": [ - 26.486160000000382, - 34.84491499999967, - 26.219829000000573, - 26.783986999999797, - 26.63177799999994 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 31, - "plateSwitches": 10, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 27.862570000000233, - "median": 28.006227000000763, - "max": 28.23831599999994 - } - } - }, - { - "id": "gomez-bedside-chest", - "title": "Сундук в ногах Гомеза", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/gomez-bedside-chest/", - "code": "SgjQQDUgCI", - "expectedActions": 23, - "solvers": { - "matrix-astar": { - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 1.5267970000004425, - "median": 1.6018199999998615, - "max": 1.966659000000618 - }, - "samplesMs": [ - 1.966659000000618, - 1.713477999999668, - 1.5267970000004425, - 1.5952500000003056, - 1.6018199999998615 - ] - }, - "bfs": { - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 1.9026420000000144, - "median": 1.977524999999332, - "max": 2.3347429999994347 - }, - "samplesMs": [ - 2.3347429999994347, - 1.9453270000003613, - 1.977524999999332, - 1.9026420000000144, - 2.076614999999947 - ] - }, - "baseline": { - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 9.919945000000553, - "median": 10.484193000000232, - "max": 13.091537000000244 - }, - "samplesMs": [ - 13.091537000000244, - 10.404543999999987, - 10.943454000000202, - 10.484193000000232, - 9.919945000000553 - ] - }, - "reference": { - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 8.14983200000006, - "median": 9.029956000000311, - "max": 9.092720000000554 - }, - "samplesMs": [ - 9.029956000000311, - 9.072660000000724, - 9.092720000000554, - 8.2767130000002, - 8.14983200000006 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 7.661264000000301, - "median": 7.950699999999415, - "max": 8.508680999999342 - } - } - }, - { - "id": "gomez-wall-chest", - "title": "Сундук у стены в покоях Гомеза", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/gomez-wall-chest/", - "code": "ZDAkKqACQECQAA", - "expectedActions": 20, - "solvers": { - "matrix-astar": { - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 5.406092999999601, - "median": 6.001978999999665, - "max": 7.071057999999539 - }, - "samplesMs": [ - 5.406092999999601, - 7.071057999999539, - 5.818043000000216, - 6.001978999999665, - 6.5075010000000475 - ] - }, - "bfs": { - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 15.09058399999958, - "median": 15.563595999999961, - "max": 16.102007000000413 - }, - "samplesMs": [ - 15.09058399999958, - 15.663696999999956, - 15.291948000000048, - 15.563595999999961, - 16.102007000000413 - ] - }, - "baseline": { - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 91.64663500000006, - "median": 93.95285500000045, - "max": 113.02099300000009 - }, - "samplesMs": [ - 113.02099300000009, - 93.95285500000045, - 91.64663500000006, - 92.53698499999973, - 104.01599600000009 - ] - }, - "reference": { - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 80.892425, - "median": 84.37700799999948, - "max": 86.19715600000018 - }, - "samplesMs": [ - 80.892425, - 83.31325800000013, - 85.81988900000033, - 84.37700799999948, - 86.19715600000018 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 83.76281300000028, - "median": 85.88850400000047, - "max": 89.70766300000014 - } - } - }, - { - "id": "voran-bedside-chest", - "title": "Сундук у кровати Ворона", - "plateCount": 7, - "url": "https://unlockmyloot.com/locks/voran-bedside-chest/", - "code": "jV46BEAAIAAAJLASAAA", - "expectedActions": 9, - "solvers": { - "matrix-astar": { - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 7, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.14533899999878486, - "median": 0.17423199999939243, - "max": 0.4060009999993781 - }, - "samplesMs": [ - 0.14533899999878486, - 0.4060009999993781, - 0.17423199999939243, - 0.20772200000101293, - 0.16797200000110024 - ] - }, - "bfs": { - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 7, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 27.983533000000534, - "median": 30.266827999999805, - "max": 41.61805600000116 - }, - "samplesMs": [ - 30.266827999999805, - 29.730952000001707, - 36.83347800000047, - 41.61805600000116, - 27.983533000000534 - ] - }, - "baseline": { - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 7, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 186.19204700000046, - "median": 190.95703200000025, - "max": 209.45192899999893 - }, - "samplesMs": [ - 196.2128190000003, - 190.95703200000025, - 209.45192899999893, - 188.73478799999975, - 186.19204700000046 - ] - }, - "reference": { - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 7, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 188.30050400000073, - "median": 203.81756099999984, - "max": 265.4217410000001 - }, - "samplesMs": [ - 207.7155449999991, - 265.4217410000001, - 203.81756099999984, - 190.18953699999838, - 188.30050400000073 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 7, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 172.0179500000013, - "median": 178.24784999999974, - "max": 182.09039500000017 - } - } - }, - { - "id": "arto-room-door", - "title": "Дверь в комнату Арто", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/arto-room-door/", - "code": "TbYwABSyBC", - "expectedActions": 7, - "solvers": { - "matrix-astar": { - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.06326499999886437, - "median": 0.07394100000055914, - "max": 0.14646999999968102 - }, - "samplesMs": [ - 0.06326499999886437, - 0.06469800000013493, - 0.14646999999968102, - 0.08190300000023853, - 0.07394100000055914 - ] - }, - "bfs": { - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.4833470000012312, - "median": 0.5335629999990488, - "max": 0.5610440000000381 - }, - "samplesMs": [ - 0.48387800000091374, - 0.4833470000012312, - 0.5335629999990488, - 0.5337839999992866, - 0.5610440000000381 - ] - }, - "baseline": { - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 2.122744000000239, - "median": 2.2399900000000343, - "max": 2.4100859999998647 - }, - "samplesMs": [ - 2.4100859999998647, - 2.2399900000000343, - 2.122744000000239, - 2.232788999999684, - 2.2581979999995383 - ] - }, - "reference": { - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - } - ], - "timingMs": { - "min": 4.410926000000472, - "median": 4.537776999999551, - "max": 6.422823999999309 - }, - "samplesMs": [ - 4.410926000000472, - 4.483815999999933, - 6.422823999999309, - 5.09459600000082, - 4.537776999999551 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - } - ], - "historicalTimingMs": { - "min": 4.352972000000591, - "median": 4.386361999999281, - "max": 4.6916220000002795 - } - } - }, - { - "id": "arto-far-bed-chest", - "title": "Сундук у дальней кровати Арто", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/arto-far-bed-chest/", - "code": "ZpEUEAAAgASIAk", - "expectedActions": 11, - "solvers": { - "matrix-astar": { - "A": 11, - "U": 5, - "C": 44, - "plateSwitches": 10, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.19944000000032247, - "median": 0.21220999999968626, - "max": 0.3316790000008041 - }, - "samplesMs": [ - 0.3316790000008041, - 0.20198400000117545, - 0.27441299999918556, - 0.21220999999968626, - 0.19944000000032247 - ] - }, - "bfs": { - "A": 11, - "U": 5, - "C": 44, - "plateSwitches": 10, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 13.547542000000249, - "median": 13.620272000000114, - "max": 17.535142000000633 - }, - "samplesMs": [ - 13.57144899999912, - 13.547542000000249, - 17.535142000000633, - 13.620272000000114, - 13.810718999999153 - ] - }, - "baseline": { - "A": 11, - "U": 5, - "C": 44, - "plateSwitches": 10, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 78.97781500000019, - "median": 79.81662699999833, - "max": 82.36540199999945 - }, - "samplesMs": [ - 79.81662699999833, - 79.46290500000032, - 81.65442199999961, - 78.97781500000019, - 82.36540199999945 - ] - }, - "reference": { - "A": 13, - "U": 5, - "C": 40, - "plateSwitches": 12, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 26.54638999999952, - "median": 26.896836999998413, - "max": 28.641020999999455 - }, - "samplesMs": [ - 26.651318000000174, - 28.156301999999414, - 26.896836999998413, - 28.641020999999455, - 26.54638999999952 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 13, - "U": 5, - "C": 40, - "plateSwitches": 12, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 25.627033000000665, - "median": 26.17454699999871, - "max": 26.764604999998483 - } - } - }, - { - "id": "arto-near-bed-chest", - "title": "Сундук у ближней кровати Арто", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/arto-near-bed-chest/", - "code": "Y2WYAEGAAIGEAY", - "expectedActions": 13, - "solvers": { - "matrix-astar": { - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.5017150000003312, - "median": 0.5202929999995831, - "max": 0.6356059999998251 - }, - "samplesMs": [ - 0.5202929999995831, - 0.6356059999998251, - 0.5104079999982787, - 0.5017150000003312, - 0.560762999999497 - ] - }, - "bfs": { - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 14.866877000000386, - "median": 15.097874999999476, - "max": 16.56613499999912 - }, - "samplesMs": [ - 15.017594000000827, - 15.502164000001358, - 16.56613499999912, - 14.866877000000386, - 15.097874999999476 - ] - }, - "baseline": { - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 87.3285990000004, - "median": 87.60512500000004, - "max": 92.91102800000044 - }, - "samplesMs": [ - 92.91102800000044, - 87.3285990000004, - 88.5518890000003, - 87.60512500000004, - 87.53706200000124 - ] - }, - "reference": { - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 69.04895699999906, - "median": 72.45650399999977, - "max": 82.42188700000042 - }, - "samplesMs": [ - 72.70658999999978, - 71.73398599999928, - 82.42188700000042, - 72.45650399999977, - 69.04895699999906 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 71.42761900000005, - "median": 72.47732999999971, - "max": 73.87815799999953 - } - } - }, - { - "id": "gomez-bedrooms-door", - "title": "Дверь на этаже спален покоев", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/gomez-bedrooms-door/", - "code": "YHayiAREIAIABE", - "expectedActions": 14, - "solvers": { - "matrix-astar": { - "A": 14, - "U": 6, - "C": 49, - "plateSwitches": 13, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 5.449187999998685, - "median": 5.570851000000403, - "max": 6.123373000000356 - }, - "samplesMs": [ - 5.570851000000403, - 5.449187999998685, - 5.473485000000437, - 5.594065999999657, - 6.123373000000356 - ] - }, - "bfs": { - "A": 14, - "U": 6, - "C": 45, - "plateSwitches": 13, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - } - ], - "timingMs": { - "min": 16.233053999998447, - "median": 16.523340000001554, - "max": 16.799676000000545 - }, - "samplesMs": [ - 16.57423700000072, - 16.233053999998447, - 16.523340000001554, - 16.331560999999056, - 16.799676000000545 - ] - }, - "baseline": { - "A": 14, - "U": 6, - "C": 45, - "plateSwitches": 13, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - } - ], - "timingMs": { - "min": 92.03947700000026, - "median": 92.59356099999968, - "max": 92.85346099999879 - }, - "samplesMs": [ - 92.03947700000026, - 92.85346099999879, - 92.23526099999981, - 92.67548399999941, - 92.59356099999968 - ] - }, - "reference": { - "A": 14, - "U": 6, - "C": 45, - "plateSwitches": 13, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 35.48603000000003, - "median": 36.05874100000074, - "max": 38.373734999999215 - }, - "samplesMs": [ - 35.48603000000003, - 35.75500500000089, - 36.05874100000074, - 38.27632799999992, - 38.373734999999215 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 14, - "U": 6, - "C": 45, - "plateSwitches": 13, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 36.83029600000009, - "median": 37.16563900000074, - "max": 42.04815899999994 - } - } - }, - { - "id": "gomez-bedrooms-far-chest", - "title": "Сундук у дальней кровати (этаж спален)", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/gomez-bedrooms-far-chest/", - "code": "RzbBFITEQQ", - "expectedActions": 14, - "solvers": { - "matrix-astar": { - "A": 14, - "U": 5, - "C": 43, - "plateSwitches": 13, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 1.4950800000005984, - "median": 1.962974000000031, - "max": 3.205933000001096 - }, - "samplesMs": [ - 1.962974000000031, - 2.7143340000002354, - 1.4950800000005984, - 1.5721560000001773, - 3.205933000001096 - ] - }, - "bfs": { - "A": 14, - "U": 5, - "C": 43, - "plateSwitches": 13, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 2.0729879999998957, - "median": 2.326961000000665, - "max": 3.976502999999866 - }, - "samplesMs": [ - 2.227611000000252, - 2.552369999999428, - 2.0729879999998957, - 2.326961000000665, - 3.976502999999866 - ] - }, - "baseline": { - "A": 14, - "U": 5, - "C": 43, - "plateSwitches": 13, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 10.662161000000197, - "median": 11.987535000000207, - "max": 16.185300999999527 - }, - "samplesMs": [ - 12.506196000000273, - 11.987535000000207, - 11.98554299999887, - 10.662161000000197, - 16.185300999999527 - ] - }, - "reference": { - "A": 16, - "U": 5, - "C": 39, - "plateSwitches": 15, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 5.977913999999146, - "median": 6.535843999999997, - "max": 8.36480499999925 - }, - "samplesMs": [ - 7.7652130000005855, - 5.977913999999146, - 6.278376000000208, - 6.535843999999997, - 8.36480499999925 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 16, - "U": 5, - "C": 39, - "plateSwitches": 15, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 5.34081900000092, - "median": 5.4789160000000265, - "max": 5.777416000000812 - } - } - }, - { - "id": "gomez-bedrooms-near-chest", - "title": "Сундук у ближней кровати (этаж спален)", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/gomez-bedrooms-near-chest/", - "code": "ZVbYAEKEBgECAA", - "expectedActions": 12, - "solvers": { - "matrix-astar": { - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.1174670000000333, - "median": 0.14508899999964342, - "max": 0.1590890000006766 - }, - "samplesMs": [ - 0.1590890000006766, - 0.14545899999939138, - 0.1174670000000333, - 0.14508899999964342, - 0.12090200000056939 - ] - }, - "bfs": { - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 11.567102999999406, - "median": 11.670178000000305, - "max": 13.297065999999177 - }, - "samplesMs": [ - 13.297065999999177, - 11.567102999999406, - 11.613171999999395, - 12.4329859999998, - 11.670178000000305 - ] - }, - "baseline": { - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 65.11633000000074, - "median": 67.71605199999976, - "max": 95.26326700000027 - }, - "samplesMs": [ - 95.26326700000027, - 65.11633000000074, - 68.37845799999923, - 67.71605199999976, - 66.34749200000078 - ] - }, - "reference": { - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 38.93337699999938, - "median": 41.40710900000158, - "max": 46.79834899999878 - }, - "samplesMs": [ - 46.79834899999878, - 38.93337699999938, - 41.40710900000158, - 41.60829600000034, - 41.130763000001025 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 40.47176899999977, - "median": 40.801266000000396, - "max": 43.21825400000125 - } - } - }, - { - "id": "raven-tower-left-chest", - "title": "Левый сундук в башне Ворона", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/raven-tower-left-chest/", - "code": "bVyMKQAAGQAAWA", - "expectedActions": 11, - "solvers": { - "matrix-astar": { - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 1.3328849999998056, - "median": 1.4743689999995695, - "max": 2.742175999999745 - }, - "samplesMs": [ - 1.3328849999998056, - 1.3447130000004108, - 2.742175999999745, - 1.4991160000008676, - 1.4743689999995695 - ] - }, - "bfs": { - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 15.65353099999993, - "median": 16.923682000000554, - "max": 19.095038999999815 - }, - "samplesMs": [ - 15.65353099999993, - 17.06224100000145, - 19.095038999999815, - 15.767813999998907, - 16.923682000000554 - ] - }, - "baseline": { - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 94.0908330000002, - "median": 94.9195090000012, - "max": 95.29427399999986 - }, - "samplesMs": [ - 95.29427399999986, - 94.9195090000012, - 95.09720700000071, - 94.74589899999955, - 94.0908330000002 - ] - }, - "reference": { - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 45.36870900000031, - "median": 47.14976799999931, - "max": 51.96910900000148 - }, - "samplesMs": [ - 47.14976799999931, - 46.880622999999105, - 49.69751200000064, - 45.36870900000031, - 51.96910900000148 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 45.528119999999035, - "median": 46.7260040000001, - "max": 47.06144800000038 - } - } - }, - { - "id": "raven-tower-right-chest", - "title": "Правый сундук в башне Ворона", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/raven-tower-right-chest/", - "code": "bEhYigAEVCQEAA", - "expectedActions": 8, - "solvers": { - "matrix-astar": { - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - } - ], - "timingMs": { - "min": 0.14451799999915238, - "median": 0.1515479999998206, - "max": 0.1800309999998717 - }, - "samplesMs": [ - 0.1493850000006205, - 0.1515479999998206, - 0.15155799999956798, - 0.1800309999998717, - 0.14451799999915238 - ] - }, - "bfs": { - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 5.5585230000015144, - "median": 6.44756100000086, - "max": 7.085629999999583 - }, - "samplesMs": [ - 7.085629999999583, - 5.919084999999541, - 6.44756100000086, - 6.700902000000497, - 5.5585230000015144 - ] - }, - "baseline": { - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 31.400143000000753, - "median": 35.7508629999993, - "max": 38.89449799999966 - }, - "samplesMs": [ - 35.7508629999993, - 37.677066000000195, - 38.89449799999966, - 32.10103700000036, - 31.400143000000753 - ] - }, - "reference": { - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 15.234651000000667, - "median": 15.675302999999985, - "max": 17.53932900000109 - }, - "samplesMs": [ - 16.070357999999032, - 17.53932900000109, - 15.675302999999985, - 15.242341999999553, - 15.234651000000667 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 15.836498000000574, - "median": 16.735591999999087, - "max": 18.030751000000237 - } - } - }, - { - "id": "raven-tower-floor2-stairs-chest", - "title": "Сундук у лестницы (2-й этаж башни)", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/raven-tower-floor2-stairs-chest/", - "code": "YqahUKAAACIIAA", - "expectedActions": 16, - "solvers": { - "matrix-astar": { - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 4.273090000000593, - "median": 4.441642999998294, - "max": 7.479363000000376 - }, - "samplesMs": [ - 4.441642999998294, - 7.479363000000376, - 4.273090000000593, - 4.88193499999943, - 4.438348000001497 - ] - }, - "bfs": { - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 13.366459999999279, - "median": 13.868455999998332, - "max": 14.56905899999947 - }, - "samplesMs": [ - 13.366459999999279, - 14.56905899999947, - 14.253684999999678, - 13.479621000000407, - 13.868455999998332 - ] - }, - "baseline": { - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 80.16143600000032, - "median": 84.43987300000117, - "max": 86.14672700000119 - }, - "samplesMs": [ - 80.16143600000032, - 84.91440700000021, - 84.43987300000117, - 81.68729200000053, - 86.14672700000119 - ] - }, - "reference": { - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 51.24391699999978, - "median": 52.77958199999921, - "max": 64.88564299999962 - }, - "samplesMs": [ - 51.81840200000079, - 55.08690099999876, - 51.24391699999978, - 64.88564299999962, - 52.77958199999921 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 53.395453000001, - "median": 54.389939999999115, - "max": 56.43529800000033 - } - } - }, - { - "id": "raven-tower-floor2-chest-2", - "title": "Второй сундук на 2-м этаже башни", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/raven-tower-floor2-chest-2/", - "code": "Q6XCAhAglI", - "expectedActions": 8, - "solvers": { - "matrix-astar": { - "A": 8, - "U": 5, - "C": 29, - "plateSwitches": 7, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.17590500000005704, - "median": 0.199640000000727, - "max": 0.2346529999995255 - }, - "samplesMs": [ - 0.199640000000727, - 0.2346529999995255, - 0.20039100000030885, - 0.17590500000005704, - 0.18819300000177464 - ] - }, - "bfs": { - "A": 8, - "U": 5, - "C": 29, - "plateSwitches": 7, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 2.012869000000137, - "median": 2.0886019999998098, - "max": 2.34657100000004 - }, - "samplesMs": [ - 2.012869000000137, - 2.34657100000004, - 2.0473299999994197, - 2.0886019999998098, - 2.0896539999994275 - ] - }, - "baseline": { - "A": 8, - "U": 5, - "C": 29, - "plateSwitches": 7, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 9.323398000000452, - "median": 9.79350599999998, - "max": 10.253747999999177 - }, - "samplesMs": [ - 9.79350599999998, - 10.253747999999177, - 9.459863000000041, - 10.128740000000107, - 9.323398000000452 - ] - }, - "reference": { - "A": 9, - "U": 5, - "C": 27, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - } - ], - "timingMs": { - "min": 2.983339000000342, - "median": 3.1965799999998126, - "max": 3.440536999998585 - }, - "samplesMs": [ - 3.1965799999998126, - 3.440536999998585, - 3.3594339999999647, - 3.0248410000003787, - 2.983339000000342 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 27, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - } - ], - "historicalTimingMs": { - "min": 3.0696099999986473, - "median": 3.0996259999992617, - "max": 3.4110639999998966 - } - } - }, - { - "id": "raven-tower-floor3-entrance-chest", - "title": "Сундук у входа (3-й этаж башни)", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/raven-tower-floor3-entrance-chest/", - "code": "QmzSSoBAAg", - "expectedActions": 14, - "solvers": { - "matrix-astar": { - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 0.6376500000005763, - "median": 0.7073239999990619, - "max": 0.7690469999997731 - }, - "samplesMs": [ - 0.7073239999990619, - 0.6376500000005763, - 0.7659220000005007, - 0.6442090000000462, - 0.7690469999997731 - ] - }, - "bfs": { - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 1.5406480000001466, - "median": 1.6406690000003437, - "max": 3.2485080000005837 - }, - "samplesMs": [ - 1.6563120000009803, - 1.5406480000001466, - 1.6406690000003437, - 1.5575529999987339, - 3.2485080000005837 - ] - }, - "baseline": { - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 7.454707000000781, - "median": 8.776225000001432, - "max": 12.391845000000103 - }, - "samplesMs": [ - 7.454707000000781, - 8.776225000001432, - 8.644166000000041, - 9.519312999998874, - 12.391845000000103 - ] - }, - "reference": { - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 6.350915999999415, - "median": 6.698889000001145, - "max": 9.02689200000168 - }, - "samplesMs": [ - 6.396123999998963, - 9.02689200000168, - 6.350915999999415, - 6.74929499999962, - 6.698889000001145 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 6.549548999999388, - "median": 6.864462999998068, - "max": 7.441822000000684 - } - } - }, - { - "id": "raven-tower-floor3-empty-chest", - "title": "Пустой сундук (3-й этаж башни)", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/raven-tower-floor3-empty-chest/", - "code": "RYIBEylAIA", - "expectedActions": 9, - "solvers": { - "matrix-astar": { - "A": 9, - "U": 5, - "C": 37, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.07011600000078033, - "median": 0.08950499999991735, - "max": 0.09559399999852758 - }, - "samplesMs": [ - 0.08950499999991735, - 0.07011600000078033, - 0.09443199999986973, - 0.09559399999852758, - 0.08529899999848567 - ] - }, - "bfs": { - "A": 9, - "U": 5, - "C": 41, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 1.4483090000012453, - "median": 1.4614280000005238, - "max": 1.8376450000014302 - }, - "samplesMs": [ - 1.4483090000012453, - 1.459606000000349, - 1.8376450000014302, - 1.4614280000005238, - 1.692786999999953 - ] - }, - "baseline": { - "A": 9, - "U": 5, - "C": 41, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 8.154039000000921, - "median": 8.421802000000753, - "max": 8.643384999999398 - }, - "samplesMs": [ - 8.154039000000921, - 8.421802000000753, - 8.604736999999659, - 8.160427000000709, - 8.643384999999398 - ] - }, - "reference": { - "A": 9, - "U": 5, - "C": 37, - "plateSwitches": 8, - "commands": [ - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 5.290108999999575, - "median": 5.552193999999872, - "max": 6.112656999999672 - }, - "samplesMs": [ - 5.4817370000000665, - 5.56495299999915, - 5.290108999999575, - 6.112656999999672, - 5.552193999999872 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 37, - "plateSwitches": 8, - "commands": [ - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 5.393829000000551, - "median": 5.431555999999546, - "max": 5.844538000001194 - } - } - }, - { - "id": "raven-tower-floor4-elixirs-chest", - "title": "Сундук с эликсирами (4-й этаж башни)", - "plateCount": 7, - "url": "https://unlockmyloot.com/locks/raven-tower-floor4-elixirs-chest/", - "code": "h24ZgUAAEAQgAwAKQAA", - "expectedActions": 8, - "solvers": { - "matrix-astar": { - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.10925399999905494, - "median": 0.1123090000000957, - "max": 0.13619499999913387 - }, - "samplesMs": [ - 0.11310999999841442, - 0.10964500000045518, - 0.13619499999913387, - 0.10925399999905494, - 0.1123090000000957 - ] - }, - "bfs": { - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 11.04016099999717, - "median": 11.22109300000011, - "max": 12.678585999998177 - }, - "samplesMs": [ - 11.987196000001859, - 11.22109300000011, - 11.050477000000683, - 11.04016099999717, - 12.678585999998177 - ] - }, - "baseline": { - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 73.12932, - "median": 75.16747799999939, - "max": 82.03915599999891 - }, - "samplesMs": [ - 75.00344000000041, - 73.12932, - 75.16747799999939, - 75.42378299999837, - 82.03915599999891 - ] - }, - "reference": { - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 107.46184699999867, - "median": 112.56941299999744, - "max": 114.66700800000035 - }, - "samplesMs": [ - 112.56941299999744, - 113.98022499999934, - 112.39501000000018, - 107.46184699999867, - 114.66700800000035 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 108.15463799999998, - "median": 113.79403699999966, - "max": 127.23316599999816 - } - } - }, - { - "id": "raven-tower-floor4-food-chest", - "title": "Сундук с едой (4-й этаж башни)", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/raven-tower-floor4-food-chest/", - "code": "aYqYiAQgCACABA", - "expectedActions": 11, - "solvers": { - "matrix-astar": { - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.267201999999088, - "median": 0.27586499999961234, - "max": 0.2955149999979767 - }, - "samplesMs": [ - 0.2955149999979767, - 0.27586499999961234, - 0.27411299999948824, - 0.28803399999742396, - 0.267201999999088 - ] - }, - "bfs": { - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 15.084485999999742, - "median": 15.373972000001231, - "max": 15.722725999999966 - }, - "samplesMs": [ - 15.084485999999742, - 15.722725999999966, - 15.57101799999873, - 15.373972000001231, - 15.149865000003047 - ] - }, - "baseline": { - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 90.67828500000178, - "median": 96.31410499999765, - "max": 109.6251830000001 - }, - "samplesMs": [ - 95.69089699999677, - 96.31410499999765, - 98.28082399999766, - 90.67828500000178, - 109.6251830000001 - ] - }, - "reference": { - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 51.32584499999939, - "median": 52.31401100000221, - "max": 71.14698800000042 - }, - "samplesMs": [ - 53.33788300000015, - 52.0905359999997, - 52.31401100000221, - 51.32584499999939, - 71.14698800000042 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 51.97651599999881, - "median": 53.12884100000156, - "max": 56.046502999997756 - } - } - }, - { - "id": "raven-tower-top-chest", - "title": "Сундук на вершине башни Ворона", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/raven-tower-top-chest/", - "code": "aMCmkAAGBBYAAA", - "expectedActions": 20, - "solvers": { - "matrix-astar": { - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 7.054034000000684, - "median": 7.251360999998724, - "max": 7.770962999999028 - }, - "samplesMs": [ - 7.054034000000684, - 7.143949000001157, - 7.691812000000937, - 7.251360999998724, - 7.770962999999028 - ] - }, - "bfs": { - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 11.989058999999543, - "median": 12.636073000001488, - "max": 14.032353999999032 - }, - "samplesMs": [ - 12.1789749999989, - 12.636073000001488, - 11.989058999999543, - 12.681780999999319, - 14.032353999999032 - ] - }, - "baseline": { - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 78.8732029999992, - "median": 82.08234200000152, - "max": 83.19212100000004 - }, - "samplesMs": [ - 78.8732029999992, - 82.08234200000152, - 78.88792499999909, - 82.49721599999975, - 83.19212100000004 - ] - }, - "reference": { - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 43.02287200000137, - "median": 43.43741500000033, - "max": 48.328645999998116 - }, - "samplesMs": [ - 48.328645999998116, - 43.43741500000033, - 43.38970399999744, - 45.51470199999676, - 43.02287200000137 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 42.69402500000069, - "median": 45.8797799999993, - "max": 46.05100699999821 - } - } - }, - { - "id": "fire-lizard-cave-chest", - "title": "Ящик у огненного ящера", - "plateCount": 6, - "url": "https://unlockmyloot.com/?lock=aDasBgBgCAACCA", - "code": "aDasBgBgCAACCA", - "expectedActions": 10, - "solvers": { - "matrix-astar": { - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.17170899999837275, - "median": 0.17570400000113295, - "max": 0.2585590000016964 - }, - "samplesMs": [ - 0.2585590000016964, - 0.1746930000008433, - 0.17170899999837275, - 0.18511800000123912, - 0.17570400000113295 - ] - }, - "bfs": { - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 10.301790000001347, - "median": 10.657054999999673, - "max": 11.986535000000003 - }, - "samplesMs": [ - 10.657054999999673, - 10.631106000000727, - 10.69336999999723, - 11.986535000000003, - 10.301790000001347 - ] - }, - "baseline": { - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 63.81029900000067, - "median": 64.97021400000085, - "max": 67.82240499999898 - }, - "samplesMs": [ - 64.27876499999911, - 64.97021400000085, - 63.81029900000067, - 67.82240499999898, - 65.38025099999868 - ] - }, - "reference": { - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 53.29116200000135, - "median": 55.654889000001276, - "max": 69.18781099999978 - }, - "samplesMs": [ - 53.29116200000135, - 69.18781099999978, - 55.41265399999975, - 55.654889000001276, - 56.53110200000083 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 52.59948500000246, - "median": 53.04343399999925, - "max": 66.56672799999797 - } - } - }, - { - "id": "silas-tavern-basement-chest", - "title": "Сундук в подвале таверны", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/silas-tavern-basement-chest/", - "code": "RrGUCRKQIQ", - "expectedActions": 9, - "solvers": { - "matrix-astar": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.08665099999780068, - "median": 0.11405100000047241, - "max": 0.12520800000129384 - }, - "samplesMs": [ - 0.08671099999992293, - 0.11868799999865587, - 0.12520800000129384, - 0.11405100000047241, - 0.08665099999780068 - ] - }, - "bfs": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 1.36113800000021, - "median": 1.583772999998473, - "max": 2.4945330000009562 - }, - "samplesMs": [ - 1.3633210000007239, - 2.4945330000009562, - 1.583772999998473, - 1.36113800000021, - 1.6200070000013511 - ] - }, - "baseline": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 6.591978000000381, - "median": 7.1919010000019625, - "max": 9.724731999998767 - }, - "samplesMs": [ - 9.724731999998767, - 6.591978000000381, - 8.430585000001884, - 7.1919010000019625, - 6.710055999999895 - ] - }, - "reference": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 3.8559820000009495, - "median": 3.980659000000742, - "max": 4.069674000002124 - }, - "samplesMs": [ - 3.8559820000009495, - 4.069674000002124, - 3.980659000000742, - 3.98505600000135, - 3.9780460000001767 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 3.8142619999998715, - "median": 3.8800910000027216, - "max": 3.9581380000017816 - } - } - }, - { - "id": "silas-tavern-storeroom-chest", - "title": "Сундук склада таверны", - "plateCount": 6, - "url": "https://unlockmyloot.com/?lock=Yt05ARCBAAARCA", - "code": "Yt05ARCBAAARCA", - "expectedActions": 9, - "solvers": { - "matrix-astar": { - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.10359600000083447, - "median": 0.10550899999725516, - "max": 0.13074699999924633 - }, - "samplesMs": [ - 0.13074699999924633, - 0.10687099999995553, - 0.1044770000007702, - 0.10359600000083447, - 0.10550899999725516 - ] - }, - "bfs": { - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 13.265008999998827, - "median": 13.68164600000091, - "max": 15.403705999997328 - }, - "samplesMs": [ - 13.265008999998827, - 15.403705999997328, - 13.898020000000542, - 13.65417399999933, - 13.68164600000091 - ] - }, - "baseline": { - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 86.41672299999846, - "median": 90.60363300000245, - "max": 94.45698000000266 - }, - "samplesMs": [ - 87.26756300000125, - 93.11861699999645, - 90.60363300000245, - 94.45698000000266, - 86.41672299999846 - ] - }, - "reference": { - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 30.445969000000332, - "median": 30.779860999999073, - "max": 31.124510999998165 - }, - "samplesMs": [ - 30.70966600000247, - 30.779860999999073, - 31.124510999998165, - 30.876908000002004, - 30.445969000000332 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 30.20962199999849, - "median": 31.75662200000079, - "max": 36.70291599999837 - } - } - }, - { - "id": "silas-tavern-storeroom-door", - "title": "Склад таверны - дверь", - "plateCount": 6, - "url": "https://unlockmyloot.com/?lock=aSnYABCIZIICCA", - "code": "aSnYABCIZIICCA", - "expectedActions": 10, - "solvers": { - "matrix-astar": { - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.17005600000265986, - "median": 0.17566400000214344, - "max": 0.19170800000210875 - }, - "samplesMs": [ - 0.19170800000210875, - 0.17694699999992736, - 0.17005600000265986, - 0.17566400000214344, - 0.17566400000214344 - ] - }, - "bfs": { - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 13.175473999999667, - "median": 13.43728800000099, - "max": 14.941330999998172 - }, - "samplesMs": [ - 13.467813000002934, - 13.43728800000099, - 14.941330999998172, - 13.175473999999667, - 13.220671999999468 - ] - }, - "baseline": { - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 75.4616700000006, - "median": 81.03830999999991, - "max": 83.86942999999883 - }, - "samplesMs": [ - 75.4616700000006, - 78.55857900000046, - 81.0876350000035, - 81.03830999999991, - 83.86942999999883 - ] - }, - "reference": { - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 22.19480399999884, - "median": 22.875538000000233, - "max": 24.6016950000012 - }, - "samplesMs": [ - 22.19480399999884, - 22.24881599999935, - 24.6016950000012, - 22.875538000000233, - 23.891546999999264 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 22.321948999997403, - "median": 23.63271099999838, - "max": 27.04742599999736 - } - } - }, - { - "id": "jan-pond-house-chest", - "title": "Сундук в домике напротив Джана", - "plateCount": 6, - "url": "https://unlockmyloot.com/?lock=aDAIAEhAAAQaAA", - "code": "aDAIAEhAAAQaAA", - "expectedActions": 8, - "solvers": { - "matrix-astar": { - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.0985690000015893, - "median": 0.10396600000240142, - "max": 0.10593000000153552 - }, - "samplesMs": [ - 0.10396600000240142, - 0.10593000000153552, - 0.0985690000015893, - 0.10307499999908032, - 0.10507800000050338 - ] - }, - "bfs": { - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 6.9588110000004235, - "median": 7.15661800000089, - "max": 8.019225999996706 - }, - "samplesMs": [ - 6.9588110000004235, - 7.351751000001968, - 7.15661800000089, - 8.019225999996706, - 7.065991999999824 - ] - }, - "baseline": { - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 44.52101699999912, - "median": 45.8115779999971, - "max": 47.468220999999176 - }, - "samplesMs": [ - 45.8115779999971, - 45.762574999997014, - 44.52101699999912, - 47.468220999999176, - 46.772163999998156 - ] - }, - "reference": { - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 47.22482500000115, - "median": 47.61862800000017, - "max": 48.63067900000169 - }, - "samplesMs": [ - 47.38332300000184, - 47.22482500000115, - 47.61862800000017, - 47.89738700000089, - 48.63067900000169 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 6, - "direction": "right", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 48.03793500000029, - "median": 48.69068799999877, - "max": 50.139368000000104 - } - } - }, - { - "id": "graham-chest", - "title": "Сундук Грэхэма", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/graham-chest/", - "code": "bE0IBAAQkBCAEg", - "expectedActions": 10, - "solvers": { - "matrix-astar": { - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - } - ], - "timingMs": { - "min": 0.10560800000166637, - "median": 0.10942399999839836, - "max": 0.13842899999872316 - }, - "samplesMs": [ - 0.13842899999872316, - 0.10942399999839836, - 0.11032600000180537, - 0.10560800000166637, - 0.10712099999727798 - ] - }, - "bfs": { - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - } - ], - "timingMs": { - "min": 16.359714000001986, - "median": 17.14464399999997, - "max": 18.1319599999988 - }, - "samplesMs": [ - 16.359714000001986, - 18.1319599999988, - 17.14464399999997, - 17.511607000000367, - 16.962910999998712 - ] - }, - "baseline": { - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - } - ], - "timingMs": { - "min": 99.46993599999769, - "median": 101.19952900000135, - "max": 103.5627940000013 - }, - "samplesMs": [ - 101.70862500000294, - 101.19952900000135, - 100.83769400000165, - 103.5627940000013, - 99.46993599999769 - ] - }, - "reference": { - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 30.6395279999997, - "median": 33.14164299999902, - "max": 36.96395399999892 - }, - "samplesMs": [ - 33.14164299999902, - 33.58286699999735, - 31.785358000000997, - 30.6395279999997, - 36.96395399999892 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 32.42070899999817, - "median": 33.52989900000102, - "max": 36.52016999999978 - } - } - }, - { - "id": "gravo-chest", - "title": "Сундук Граво", - "plateCount": 5, - "url": "https://unlockmyloot.com/?lock=SkCAAjAhAg", - "code": "SkCAAjAhAg", - "expectedActions": 8, - "solvers": { - "matrix-astar": { - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.06116199999814853, - "median": 0.07165800000075251, - "max": 0.11318000000028405 - }, - "samplesMs": [ - 0.06975500000044121, - 0.11318000000028405, - 0.07165800000075251, - 0.08374600000024657, - 0.06116199999814853 - ] - }, - "bfs": { - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 1.0047220000014931, - "median": 1.1070859999999811, - "max": 1.1677870000021358 - }, - "samplesMs": [ - 1.1070859999999811, - 1.1483979999975418, - 1.1677870000021358, - 1.0047220000014931, - 1.046704000000318 - ] - }, - "baseline": { - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 5.29304399999819, - "median": 5.463670000001002, - "max": 5.936682000003202 - }, - "samplesMs": [ - 5.463260000000446, - 5.463670000001002, - 5.936682000003202, - 5.29304399999819, - 5.673746000000392 - ] - }, - "reference": { - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 5.050747999997839, - "median": 5.359533000002557, - "max": 5.448867999999493 - }, - "samplesMs": [ - 5.359533000002557, - 5.111549999997806, - 5.448867999999493, - 5.422327999996924, - 5.050747999997839 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 5.467348999998649, - "median": 8.080862999999226, - "max": 8.312551999999414 - } - } - }, - { - "id": "diego-chest", - "title": "Сундук Диего", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/diego-chest/", - "code": "Yix4ABIAASCSAE", - "expectedActions": 11, - "solvers": { - "matrix-astar": { - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.1599099999984901, - "median": 0.16224399999919115, - "max": 0.21563400000013644 - }, - "samplesMs": [ - 0.16224399999919115, - 0.16279500000018743, - 0.21563400000013644, - 0.16081199999825913, - 0.1599099999984901 - ] - }, - "bfs": { - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 12.245143000000098, - "median": 12.377211999999417, - "max": 13.043719000001147 - }, - "samplesMs": [ - 12.968141000001197, - 12.377211999999417, - 12.245143000000098, - 13.043719000001147, - 12.376984999998967 - ] - }, - "baseline": { - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 79.94461199999932, - "median": 82.86884199999986, - "max": 86.73401899999953 - }, - "samplesMs": [ - 80.7599369999989, - 85.23674200000096, - 79.94461199999932, - 86.73401899999953, - 82.86884199999986 - ] - }, - "reference": { - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 28.88734299999851, - "median": 29.897691999998642, - "max": 32.130709999997634 - }, - "samplesMs": [ - 30.00152800000069, - 29.897691999998642, - 28.88734299999851, - 32.130709999997634, - 29.73279299999922 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 30.864245999997365, - "median": 31.402186000002985, - "max": 48.39241899999979 - } - } - }, - { - "id": "grim-chest", - "title": "Сундук Грима", - "plateCount": 6, - "url": "https://unlockmyloot.com/?lock=aFhwAAABhgAAKY", - "code": "aFhwAAABhgAAKY", - "expectedActions": 8, - "solvers": { - "matrix-astar": { - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "left", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - } - ], - "timingMs": { - "min": 0.19919999999910942, - "median": 0.21358099999997648, - "max": 0.5288360000013199 - }, - "samplesMs": [ - 0.5288360000013199, - 0.19919999999910942, - 0.21358099999997648, - 0.21277999999801978, - 0.25489399999787565 - ] - }, - "bfs": { - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - } - ], - "timingMs": { - "min": 16.70064099999945, - "median": 16.907052000002295, - "max": 18.926580999999715 - }, - "samplesMs": [ - 16.907052000002295, - 17.332852999999886, - 18.926580999999715, - 16.881413999999495, - 16.70064099999945 - ] - }, - "baseline": { - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - } - ], - "timingMs": { - "min": 100.21867100000236, - "median": 106.55781000000206, - "max": 116.94362900000124 - }, - "samplesMs": [ - 109.42307100000107, - 116.94362900000124, - 100.68139800000063, - 100.21867100000236, - 106.55781000000206 - ] - }, - "reference": { - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 19.493484999999055, - "median": 20.138954999998532, - "max": 20.343853999998828 - }, - "samplesMs": [ - 20.343853999998828, - 20.32147000000259, - 20.138954999998532, - 20.00796899999841, - 19.493484999999055 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 5 - }, - { - "plate": 6, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 20.744904999999562, - "median": 21.38251400000081, - "max": 25.32780400000047 - } - } - }, - { - "id": "mercenary-near-grim-chest", - "title": "Сундук наёмника (у Грима)", - "plateCount": 6, - "url": "https://unlockmyloot.com/?lock=ZA4gEWAAAAASAQ", - "code": "ZA4gEWAAAAASAQ", - "expectedActions": 8, - "solvers": { - "matrix-astar": { - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - } - ], - "timingMs": { - "min": 0.09175800000230083, - "median": 0.09823799999867333, - "max": 0.1189090000007127 - }, - "samplesMs": [ - 0.09823799999867333, - 0.1189090000007127, - 0.10301600000093458, - 0.09668500000043423, - 0.09175800000230083 - ] - }, - "bfs": { - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - } - ], - "timingMs": { - "min": 15.047442999999475, - "median": 15.120403000000806, - "max": 15.504441000000952 - }, - "samplesMs": [ - 15.047442999999475, - 15.120403000000806, - 15.259673000000475, - 15.504441000000952, - 15.074404999999388 - ] - }, - "baseline": { - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - } - ], - "timingMs": { - "min": 93.2197699999997, - "median": 96.11985300000015, - "max": 100.79693100000077 - }, - "samplesMs": [ - 94.04025300000285, - 93.2197699999997, - 96.11985300000015, - 97.28438499999902, - 100.79693100000077 - ] - }, - "reference": { - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 28.609511999999086, - "median": 29.191116999998485, - "max": 34.457431999999244 - }, - "samplesMs": [ - 29.191116999998485, - 29.199329999999463, - 34.457431999999244, - 28.609511999999086, - 28.962954000002355 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 2 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 1 - }, - { - "plate": 3, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 30.25687200000175, - "median": 31.47373399999924, - "max": 35.279522999997425 - } - } - }, - { - "id": "popular-lock", - "title": "Популярный замок", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/popular-lock/", - "code": "TAmBLEwMAE", - "expectedActions": 13, - "solvers": { - "matrix-astar": { - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.5506289999975706, - "median": 0.5712390000007872, - "max": 0.592822000002343 - }, - "samplesMs": [ - 0.5820249999997031, - 0.592822000002343, - 0.5712390000007872, - 0.5506289999975706, - 0.5536530000026687 - ] - }, - "bfs": { - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 1.9107960000001185, - "median": 1.9221920000018144, - "max": 1.9523069999995641 - }, - "samplesMs": [ - 1.9221920000018144, - 1.9523069999995641, - 1.932487999998557, - 1.9211009999999078, - 1.9107960000001185 - ] - }, - "baseline": { - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 1, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 9.808650999999372, - "median": 9.924764999999752, - "max": 10.116053000001557 - }, - "samplesMs": [ - 9.808650999999372, - 9.924764999999752, - 10.116053000001557, - 9.954040000000532, - 9.852795999999216 - ] - }, - "reference": { - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 5.52619599999889, - "median": 5.561628999999812, - "max": 5.680867999999464 - }, - "samplesMs": [ - 5.567137000001821, - 5.52619599999889, - 5.680867999999464, - 5.561628999999812, - 5.544122999999672 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 6.609628999998677, - "median": 8.722789999999804, - "max": 12.860713000001851 - } - } - }, - { - "id": "alberto-mine-hut-chest", - "title": "Сундук в хижине шахты Альберто", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/alberto-mine-hut-chest/", - "code": "ULWBAQhAKg", - "expectedActions": 9, - "solvers": { - "matrix-astar": { - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.0598100000024715, - "median": 0.065547999998671, - "max": 0.09760699999969802 - }, - "samplesMs": [ - 0.07305999999880441, - 0.065547999998671, - 0.0598100000024715, - 0.06335500000204775, - 0.09760699999969802 - ] - }, - "bfs": { - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 1.7664679999979853, - "median": 1.77728500000012, - "max": 1.8063279999987572 - }, - "samplesMs": [ - 1.7774649999992107, - 1.7723970000006375, - 1.7664679999979853, - 1.77728500000012, - 1.8063279999987572 - ] - }, - "baseline": { - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 2, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 8.646631999999954, - "median": 9.241656999998668, - "max": 10.841904999997496 - }, - "samplesMs": [ - 10.841904999997496, - 8.646631999999954, - 9.251651999999012, - 8.938310000001366, - 9.241656999998668 - ] - }, - "reference": { - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 5.200595999998768, - "median": 5.284802000001946, - "max": 5.329430000001594 - }, - "samplesMs": [ - 5.200595999998768, - 5.329430000001594, - 5.2918529999988095, - 5.284802000001946, - 5.225623999998788 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 4 - }, - { - "plate": 5, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 5 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 5.769542999998521, - "median": 6.056866999999329, - "max": 6.331880999998248 - } - } - }, - { - "id": "alberto-mine-upstairs-left-chest", - "title": "Левый сундук наверху в шахте Альберто", - "plateCount": 4, - "url": "https://unlockmyloot.com/locks/alberto-mine-upstairs-left-chest/", - "code": "MEAIAho", - "expectedActions": 4, - "solvers": { - "matrix-astar": { - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.015703999997640494, - "median": 0.018407999999908498, - "max": 0.030525999998644693 - }, - "samplesMs": [ - 0.018407999999908498, - 0.019089000001258682, - 0.015703999997640494, - 0.030525999998644693, - 0.01837800000066636 - ] - }, - "bfs": { - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.013030000001890585, - "median": 0.014802000001509441, - "max": 0.015342999999120366 - }, - "samplesMs": [ - 0.015342999999120366, - 0.01387099999919883, - 0.013030000001890585, - 0.014802000001509441, - 0.014901999998983229 - ] - }, - "baseline": { - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.034911999999167165, - "median": 0.038548000000446336, - "max": 0.04640999999901396 - }, - "samplesMs": [ - 0.039508999998361105, - 0.038548000000446336, - 0.034911999999167165, - 0.04640999999901396, - 0.037846999999601394 - ] - }, - "reference": { - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.1981679999989865, - "median": 0.2100959999988845, - "max": 0.23570499999914318 - }, - "samplesMs": [ - 0.2100959999988845, - 0.2263409999977739, - 0.1981679999989865, - 0.23570499999914318, - 0.20336600000155158 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - { - "plate": 2, - "direction": "left", - "steps": 5 - }, - { - "plate": 3, - "direction": "left", - "steps": 6 - }, - { - "plate": 2, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 0.34879500000170083, - "median": 0.37524499999926775, - "max": 0.3974180000004708 - } - } - }, - { - "id": "alberto-mine-upstairs-right-chest", - "title": "Правый сундук наверху в шахте Альберто", - "plateCount": 5, - "url": "https://unlockmyloot.com/locks/alberto-mine-upstairs-right-chest/", - "code": "UZ1wAFAKxU", - "expectedActions": 5, - "solvers": { - "matrix-astar": { - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 0.026289000001270324, - "median": 0.032729000002291286, - "max": 0.05053599999882863 - }, - "samplesMs": [ - 0.05053599999882863, - 0.029293999999936204, - 0.03702599999814993, - 0.032729000002291286, - 0.026289000001270324 - ] - }, - "bfs": { - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 0.4969170000003942, - "median": 0.5107790000001842, - "max": 0.644560000000638 - }, - "samplesMs": [ - 0.5082340000008116, - 0.4969170000003942, - 0.644560000000638, - 0.561706000000413, - 0.5107790000001842 - ] - }, - "baseline": { - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 2.1250879999970493, - "median": 2.144357000001037, - "max": 2.69321299999865 - }, - "samplesMs": [ - 2.144357000001037, - 2.69321299999865, - 2.125657999997202, - 2.1250879999970493, - 2.1700160000000324 - ] - }, - "reference": { - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - } - ], - "timingMs": { - "min": 1.5175940000008268, - "median": 1.549391000000469, - "max": 1.5679799999998068 - }, - "samplesMs": [ - 1.549391000000469, - 1.561029000000417, - 1.5679799999998068, - 1.5377640000006068, - 1.5175940000008268 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "left", - "steps": 2 - }, - { - "plate": 2, - "direction": "left", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 4 - }, - { - "plate": 4, - "direction": "left", - "steps": 2 - } - ], - "historicalTimingMs": { - "min": 1.5827119999994466, - "median": 1.7594979999994393, - "max": 1.899319000000105 - } - } - }, - { - "id": "cavalorn-storeroom-door", - "title": "Дверь на склад охотника Кавалорна", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/cavalorn-storeroom-door/", - "code": "cuaSQAAQgoQJQA", - "expectedActions": 11, - "solvers": { - "matrix-astar": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 0.23629499999879044, - "median": 0.2387090000011085, - "max": 0.24234400000204914 - }, - "samplesMs": [ - 0.23906999999962864, - 0.24234400000204914, - 0.23629499999879044, - 0.2387090000011085, - 0.2364159999997355 - ] - }, - "bfs": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 13.715700000000652, - "median": 13.902150000001711, - "max": 14.243180000001303 - }, - "samplesMs": [ - 13.715700000000652, - 13.902150000001711, - 14.200926000001346, - 14.243180000001303, - 13.861504999997123 - ] - }, - "baseline": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 1 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 1, - "direction": "right", - "steps": 4 - }, - { - "plate": 4, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 4 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 79.4916000000012, - "median": 81.64077299999917, - "max": 90.80175600000075 - }, - "samplesMs": [ - 79.4916000000012, - 82.5692220000019, - 81.08390599999984, - 90.80175600000075, - 81.64077299999917 - ] - }, - "reference": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "timingMs": { - "min": 40.194054999999935, - "median": 40.40897699999914, - "max": 42.25605799999903 - }, - "samplesMs": [ - 42.25605799999903, - 40.194054999999935, - 41.53761099999974, - 40.40897699999914, - 40.382708000001 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - { - "plate": 4, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "right", - "steps": 1 - }, - { - "plate": 6, - "direction": "left", - "steps": 4 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "right", - "steps": 2 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 4, - "direction": "right", - "steps": 6 - }, - { - "plate": 6, - "direction": "left", - "steps": 6 - }, - { - "plate": 5, - "direction": "right", - "steps": 3 - }, - { - "plate": 2, - "direction": "right", - "steps": 6 - }, - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 40.57944999999745, - "median": 42.616525000001275, - "max": 43.05461400000058 - } - } - }, - { - "id": "old-to-new-camp-bandits-chest", - "title": "Сундук у разбойников на пути в Новый лагерь", - "plateCount": 6, - "url": "https://unlockmyloot.com/locks/old-to-new-camp-bandits-chest/", - "code": "cOEMSCgBGIQAAA", - "expectedActions": 10, - "solvers": { - "matrix-astar": { - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 0.4119699999973818, - "median": 0.4313090000032389, - "max": 0.43824899999890476 - }, - "samplesMs": [ - 0.4313090000032389, - 0.43735799999922165, - 0.4185300000026473, - 0.43824899999890476, - 0.4119699999973818 - ] - }, - "bfs": { - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 9.617881000001944, - "median": 9.699493000000075, - "max": 11.34095299999899 - }, - "samplesMs": [ - 9.699493000000075, - 9.617881000001944, - 10.828151000001526, - 9.671301000002131, - 11.34095299999899 - ] - }, - "baseline": { - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 6, - "direction": "right", - "steps": 2 - } - ], - "timingMs": { - "min": 61.399960000002466, - "median": 66.13834900000074, - "max": 66.89568900000086 - }, - "samplesMs": [ - 66.13834900000074, - 61.399960000002466, - 66.1904570000006, - 62.48275899999862, - 66.89568900000086 - ] - }, - "reference": { - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "timingMs": { - "min": 24.716668000000936, - "median": 25.8066370000015, - "max": 27.208236999998917 - }, - "samplesMs": [ - 25.281278000002203, - 24.716668000000936, - 26.408062000002246, - 27.208236999998917, - 25.8066370000015 - ] - } - }, - "historicalReference": { - "kind": "cached-observation", - "capturedAt": "2026-09-07T06:02:47.149Z", - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - { - "plate": 6, - "direction": "right", - "steps": 2 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 3, - "direction": "right", - "steps": 1 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - }, - { - "plate": 5, - "direction": "left", - "steps": 6 - }, - { - "plate": 4, - "direction": "left", - "steps": 3 - }, - { - "plate": 3, - "direction": "right", - "steps": 3 - }, - { - "plate": 1, - "direction": "left", - "steps": 3 - } - ], - "historicalTimingMs": { - "min": 24.668550999998843, - "median": 24.905797999999777, - "max": 25.209614999999758 - } - } - } - ], - "limitations": [ - "These are 45 fixed catalog configurations, not a random sample or a claim about every game state.", - "Only actions (A) are minimized by the production solver; distinct plates (U) and unit divisions (C) are descriptive metrics.", - "The reference minimizes unit divisions before plate switches; its objective differs from minimum grouped actions.", - "Timing reflects this runtime, machine, sample count, JIT and garbage collection; no timing threshold is a correctness gate.", - "These are Node.js timings, not measurements in an actual browser or Web Worker.", - "Cached historical timings are provenance only and are excluded from current speed comparisons.", - "The sum of per-lock medians is an aggregate of medians, not the median runtime of a whole-catalog pass.", - "Speedup ratios above 1 favor matrix A*; ratios below 1 favor the comparator. Timing ties require equal measured medians." - ] -} diff --git a/docs/benchmarks/run-20260907T070258001Z-f78dff13/benchmark.md b/docs/benchmarks/run-20260907T070258001Z-f78dff13/benchmark.md deleted file mode 100644 index 939792d..0000000 --- a/docs/benchmarks/run-20260907T070258001Z-f78dff13/benchmark.md +++ /dev/null @@ -1,239 +0,0 @@ -# Matrix search benchmark - -Generated: 2026-09-07T07:02:58.001Z. Scope: **full-45-catalog** (45 locks). - -Correctness gates: **PASS**. Warmups per solver/lock: 1; measured repetitions: 5. - -A = actions (grouped commands); U = unique selected plates; C = unit divisions of the selected plate. All timings are milliseconds. - -Only the synchronous solver call is timed. Input cloning, reference click grouping, replay, metrics, and reporting are outside timing. Current and baseline calls include their public facade validation/result construction. - -Per lock, warmup and measured rounds rotate solver order deterministically. - -GC: Natural Node.js garbage collection; no forced GC. - -## Runtime and source evidence - -```json -{ - "environment": { - "node": "v24.19.0", - "v8": "13.6.233.17-node.51", - "platform": "linux", - "arch": "x64", - "osRelease": "6.18.35", - "cpu": "AMD EPYC 9V74 80-Core Processor", - "logicalCpus": 9, - "totalMemoryBytes": 23109910528, - "execArgv": [] - }, - "source": { - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", - "dirty": false, - "core": { - "sha256": "064dd862f17ea1a8fdd00abd4e06dfb024ffc4dcf98cb648c881a163c4599079", - "files": { - "cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", - "index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", - "matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", - "matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", - "result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", - "search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", - "solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", - "state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" - } - }, - "harness": { - "sha256": "becf8b6ca5b15dd64d91e52939bb1bf8b46816f4f62e70b27629e17c26f9bde4", - "files": { - "baseline/src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", - "baseline/src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", - "baseline/src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "baseline/src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", - "baseline/src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "baseline/src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", - "catalog.mjs": "1aeead30de3b4041309df18edae2daa206212cf6bb5e8b7031e77743d450a497", - "harness.mjs": "49c73bd6cd5b096f079446ca89a272b73f63e333ac187f814e126d66198ed2a0", - "validation.mjs": "3162b5374be266e5963e63a494006666c2b29ff2be10f2d2b58c8c57e036759e" - }, - "cliSha256": "57276d8b7dfbc48c8348d021b443f64d14c3bb3dbce753db8d54d51ee2fbf76e" - } - }, - "fixtures": { - "count": 45, - "catalogSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", - "manifestSha256": "a634d5328fd8d41470397b622ae0bd32b8aac0355dfc31975e023a63a62e65c9", - "repository": "https://github.com/1h8s/unlockmyloot", - "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365" - }, - "baseline": { - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "a87e739a22ccc4215d6d9b14fa06b0738171cb60", - "license": "Apache-2.0", - "files": { - "src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", - "src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", - "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", - "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", - "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" - }, - "notes": "Unmodified source snapshots; imports remain relative within this directory. No upstream UnlockMyLoot solver code is included." - }, - "reference": { - "measured": true, - "repository": "https://github.com/1h8s/unlockmyloot", - "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "path": "index.html", - "fileSha256": "691f744099a9169f30e9883fddc5fdbb4949d306f3011e601678419d7b93821b", - "solverBlockSha256": "d5bc85f40fd7c5b61097403df399ecb7dfd2b58924dc7a670aa0d05306017380", - "execution": "Unchanged source compiled with Function in the same Node.js realm; no browser or vm context." - } -} -``` - -## Totals across measured locks - -| Solver | A | U | C | Sum of per-lock median ms | -| --- | ---: | ---: | ---: | ---: | -| Matrix A* | 483 | 245 | 1723 | 49.630951 | -| Current exact BFS | 483 | 245 | 1727 | 423.230535 | -| Original Apache BFS | 483 | 245 | 1727 | 2537.917176 | -| UnlockMyLoot (live pinned reference) | 491 | 245 | 1695 | 1465.336812 | - -## Matrix A* comparisons - -Wins/ties/losses count locks where matrix A* is lower/equal/higher. A ratio above 1 favors matrix A*. - -| Comparator | A wins/ties/losses | U wins/ties/losses | C wins/ties/losses | Time wins/ties/losses | Geometric mean speedup | Ratio of summed medians | -| --- | --- | --- | --- | --- | ---: | ---: | -| Current exact BFS | 0/45/0 | 0/45/0 | 2/42/1 | 43/0/2 | 16.232012 | 8.527552 | -| Original Apache BFS | 0/45/0 | 0/45/0 | 2/42/1 | 45/0/0 | 88.393103 | 51.135776 | -| UnlockMyLoot (live pinned reference) | 5/40/0 | 0/45/0 | 0/39/6 | 45/0/0 | 52.863128 | 29.524657 | -| Reference (cached metrics only) | 5/40/0 | 0/45/0 | 0/39/6 | not timed | — | — | - -## Per-lock action metrics - -| Lock | N | Exact minimum A | Matrix A* A/U/C | Current exact BFS A/U/C | Original Apache BFS A/U/C | UnlockMyLoot (live pinned reference) A/U/C | Cached reference A/U/C | -| --- | ---: | ---: | --- | --- | --- | --- | --- | -| Дверь Разрушенной башни (ruined-tower-door) | 6 | 17 | 17/6/57 | 17/6/57 | 17/6/57 | 17/6/57 | 17/6/57 | -| Сундук Скатти (scatty-chest) | 5 | 9 | 9/5/31 | 9/5/31 | 9/5/31 | 9/5/31 | 9/5/31 | -| Сундук Кор Галома (cor-galom-chest) | 4 | 5 | 5/4/15 | 5/4/19 | 5/4/19 | 5/4/15 | 5/4/15 | -| Сундук в спальне Кор Галома (cor-galom-bedroom-chest) | 6 | 14 | 14/5/60 | 14/5/60 | 14/5/60 | 14/5/60 | 14/5/60 | -| Сундук в кузнице Болотного лагеря (swamp-camp-forge-chest) | 6 | 11 | 11/6/41 | 11/6/41 | 11/6/41 | 11/6/41 | 11/6/41 | -| Склад Рисового лорда (3-й этаж) (rice-lord-storeroom) | 5 | 9 | 9/5/30 | 9/5/30 | 9/5/30 | 9/5/30 | 9/5/30 | -| Дверь в покои Гомеза (gomez-chambers-door) | 6 | 9 | 9/6/28 | 9/6/28 | 9/6/28 | 9/6/28 | 9/6/28 | -| Сундук в тронном зале у Гомеза (gomez-throne-hall-chest) | 4 | 5 | 5/4/16 | 5/4/16 | 5/4/16 | 5/4/16 | 5/4/16 | -| Дверь в темнице с расстрелянным призраком (old-camp-dungeon-door) | 6 | 17 | 17/6/72 | 17/6/72 | 17/6/72 | 18/6/68 | 18/6/68 | -| Ключ от темницы Арлина (arlin-dungeon-key) | 7 | 7 | 7/5/25 | 7/5/25 | 7/5/25 | 7/5/25 | 7/5/25 | -| Винный погреб Гомеза (gomez-wine-cellar) | 6 | 9 | 9/6/41 | 9/6/41 | 9/6/41 | 11/6/31 | 11/6/31 | -| Сундук в ногах Гомеза (gomez-bedside-chest) | 5 | 23 | 23/5/68 | 23/5/68 | 23/5/68 | 23/5/68 | 23/5/68 | -| Сундук у стены в покоях Гомеза (gomez-wall-chest) | 6 | 20 | 20/6/75 | 20/6/75 | 20/6/75 | 20/6/75 | 20/6/75 | -| Сундук у кровати Ворона (voran-bedside-chest) | 7 | 9 | 9/7/38 | 9/7/38 | 9/7/38 | 9/7/38 | 9/7/38 | -| Дверь в комнату Арто (arto-room-door) | 5 | 7 | 7/5/31 | 7/5/31 | 7/5/31 | 7/5/31 | 7/5/31 | -| Сундук у дальней кровати Арто (arto-far-bed-chest) | 6 | 11 | 11/5/44 | 11/5/44 | 11/5/44 | 13/5/40 | 13/5/40 | -| Сундук у ближней кровати Арто (arto-near-bed-chest) | 6 | 13 | 13/6/52 | 13/6/52 | 13/6/52 | 13/6/52 | 13/6/52 | -| Дверь на этаже спален покоев (gomez-bedrooms-door) | 6 | 14 | 14/6/49 | 14/6/45 | 14/6/45 | 14/6/45 | 14/6/45 | -| Сундук у дальней кровати (этаж спален) (gomez-bedrooms-far-chest) | 5 | 14 | 14/5/43 | 14/5/43 | 14/5/43 | 16/5/39 | 16/5/39 | -| Сундук у ближней кровати (этаж спален) (gomez-bedrooms-near-chest) | 6 | 12 | 12/6/56 | 12/6/56 | 12/6/56 | 12/6/56 | 12/6/56 | -| Левый сундук в башне Ворона (raven-tower-left-chest) | 6 | 11 | 11/6/35 | 11/6/35 | 11/6/35 | 11/6/35 | 11/6/35 | -| Правый сундук в башне Ворона (raven-tower-right-chest) | 6 | 8 | 8/6/23 | 8/6/23 | 8/6/23 | 8/6/23 | 8/6/23 | -| Сундук у лестницы (2-й этаж башни) (raven-tower-floor2-stairs-chest) | 6 | 16 | 16/6/48 | 16/6/48 | 16/6/48 | 16/6/48 | 16/6/48 | -| Второй сундук на 2-м этаже башни (raven-tower-floor2-chest-2) | 5 | 8 | 8/5/29 | 8/5/29 | 8/5/29 | 9/5/27 | 9/5/27 | -| Сундук у входа (3-й этаж башни) (raven-tower-floor3-entrance-chest) | 5 | 14 | 14/5/44 | 14/5/44 | 14/5/44 | 14/5/44 | 14/5/44 | -| Пустой сундук (3-й этаж башни) (raven-tower-floor3-empty-chest) | 5 | 9 | 9/5/37 | 9/5/41 | 9/5/41 | 9/5/37 | 9/5/37 | -| Сундук с эликсирами (4-й этаж башни) (raven-tower-floor4-elixirs-chest) | 7 | 8 | 8/5/36 | 8/5/36 | 8/5/36 | 8/5/36 | 8/5/36 | -| Сундук с едой (4-й этаж башни) (raven-tower-floor4-food-chest) | 6 | 11 | 11/6/43 | 11/6/43 | 11/6/43 | 11/6/43 | 11/6/43 | -| Сундук на вершине башни Ворона (raven-tower-top-chest) | 6 | 20 | 20/6/48 | 20/6/48 | 20/6/48 | 20/6/48 | 20/6/48 | -| Ящик у огненного ящера (fire-lizard-cave-chest) | 6 | 10 | 10/6/41 | 10/6/41 | 10/6/41 | 10/6/41 | 10/6/41 | -| Сундук в подвале таверны (silas-tavern-basement-chest) | 5 | 9 | 9/5/30 | 9/5/30 | 9/5/30 | 9/5/30 | 9/5/30 | -| Сундук склада таверны (silas-tavern-storeroom-chest) | 6 | 9 | 9/6/31 | 9/6/31 | 9/6/31 | 9/6/31 | 9/6/31 | -| Склад таверны - дверь (silas-tavern-storeroom-door) | 6 | 10 | 10/5/31 | 10/5/31 | 10/5/31 | 10/5/31 | 10/5/31 | -| Сундук в домике напротив Джана (jan-pond-house-chest) | 6 | 8 | 8/6/33 | 8/6/33 | 8/6/33 | 8/6/33 | 8/6/33 | -| Сундук Грэхэма (graham-chest) | 6 | 10 | 10/6/40 | 10/6/40 | 10/6/40 | 10/6/40 | 10/6/40 | -| Сундук Граво (gravo-chest) | 5 | 8 | 8/5/27 | 8/5/27 | 8/5/27 | 8/5/27 | 8/5/27 | -| Сундук Диего (diego-chest) | 6 | 11 | 11/6/40 | 11/6/40 | 11/6/40 | 11/6/40 | 11/6/40 | -| Сундук Грима (grim-chest) | 6 | 8 | 8/6/21 | 8/6/21 | 8/6/21 | 8/6/21 | 8/6/21 | -| Сундук наёмника (у Грима) (mercenary-near-grim-chest) | 6 | 8 | 8/6/25 | 8/6/25 | 8/6/25 | 8/6/25 | 8/6/25 | -| Популярный замок (popular-lock) | 5 | 13 | 13/5/46 | 13/5/46 | 13/5/46 | 13/5/46 | 13/5/46 | -| Сундук в хижине шахты Альберто (alberto-mine-hut-chest) | 5 | 9 | 9/5/36 | 9/5/36 | 9/5/36 | 9/5/36 | 9/5/36 | -| Левый сундук наверху в шахте Альберто (alberto-mine-upstairs-left-chest) | 4 | 4 | 4/3/20 | 4/3/20 | 4/3/20 | 4/3/20 | 4/3/20 | -| Правый сундук наверху в шахте Альберто (alberto-mine-upstairs-right-chest) | 5 | 5 | 5/5/13 | 5/5/13 | 5/5/13 | 5/5/13 | 5/5/13 | -| Дверь на склад охотника Кавалорна (cavalorn-storeroom-door) | 6 | 11 | 11/6/41 | 11/6/41 | 11/6/41 | 11/6/41 | 11/6/41 | -| Сундук у разбойников на пути в Новый лагерь (old-to-new-camp-bandits-chest) | 6 | 10 | 10/5/33 | 10/5/33 | 10/5/33 | 10/5/33 | 10/5/33 | - -## Per-lock live timings (min / median / max ms) - -| Lock | Matrix A* | Current exact BFS | Original Apache BFS | UnlockMyLoot (live pinned reference) | -| --- | --- | --- | --- | --- | -| Дверь Разрушенной башни (ruined-tower-door) | 3.453076 / 4.894905 / 9.461225 | 13.383326 / 15.879230 / 25.095196 | 77.485881 / 89.626475 / 108.147190 | 79.482874 / 85.138514 / 124.039541 | -| Сундук Скатти (scatty-chest) | 0.142525 / 0.196035 / 0.249545 | 2.278478 / 2.334082 / 3.282669 | 11.299079 / 11.719832 / 14.038151 | 5.317440 / 8.441120 / 15.322403 | -| Сундук Кор Галома (cor-galom-chest) | 0.042824 / 0.055604 / 0.084437 | 0.126440 / 0.129364 / 0.160211 | 0.452561 / 0.472901 / 0.491039 | 0.302695 / 0.322104 / 0.380232 | -| Сундук в спальне Кор Галома (cor-galom-bedroom-chest) | 0.246551 / 0.326311 / 0.409457 | 12.282660 / 12.730003 / 13.442365 | 70.649905 / 72.507640 / 74.428861 | 29.965104 / 30.333097 / 38.597862 | -| Сундук в кузнице Болотного лагеря (swamp-camp-forge-chest) | 0.259761 / 0.270167 / 0.325390 | 17.623635 / 17.761072 / 18.015034 | 105.264304 / 107.644315 / 113.353715 | 35.309474 / 36.690352 / 40.805724 | -| Склад Рисового лорда (3-й этаж) (rice-lord-storeroom) | 0.134772 / 0.161573 / 0.173140 | 1.684364 / 1.698796 / 1.949533 | 8.109772 / 8.252086 / 8.363103 | 4.524297 / 4.608884 / 4.867022 | -| Дверь в покои Гомеза (gomez-chambers-door) | 0.215093 / 0.223496 / 0.400593 | 13.604608 / 15.005065 / 17.201530 | 75.371168 / 80.544472 / 83.780350 | 28.923346 / 30.335060 / 35.090975 | -| Сундук в тронном зале у Гомеза (gomez-throne-hall-chest) | 0.026100 / 0.032078 / 0.038978 | 0.048132 / 0.059740 / 0.069184 | 0.172279 / 0.174662 / 0.182124 | 0.217517 / 0.232931 / 0.250357 | -| Дверь в темнице с расстрелянным призраком (old-camp-dungeon-door) | 2.305849 / 2.374432 / 2.994916 | 14.948962 / 15.437987 / 19.826901 | 82.738222 / 85.220486 / 92.245296 | 39.820161 / 40.373513 / 44.692852 | -| Ключ от темницы Арлина (arlin-dungeon-key) | 7.175456 / 7.301655 / 8.008499 | 7.162446 / 7.257570 / 7.964863 | 43.223462 / 44.592170 / 48.960672 | 20.249175 / 20.278920 / 24.130546 | -| Винный погреб Гомеза (gomez-wine-cellar) | 0.129154 / 0.139761 / 0.273812 | 8.284354 / 8.696474 / 9.232171 | 53.113420 / 53.189675 / 56.717633 | 26.219829 / 26.631778 / 34.844915 | -| Сундук в ногах Гомеза (gomez-bedside-chest) | 1.526797 / 1.601820 / 1.966659 | 1.902642 / 1.977525 / 2.334743 | 9.919945 / 10.484193 / 13.091537 | 8.149832 / 9.029956 / 9.092720 | -| Сундук у стены в покоях Гомеза (gomez-wall-chest) | 5.406093 / 6.001979 / 7.071058 | 15.090584 / 15.563596 / 16.102007 | 91.646635 / 93.952855 / 113.020993 | 80.892425 / 84.377008 / 86.197156 | -| Сундук у кровати Ворона (voran-bedside-chest) | 0.145339 / 0.174232 / 0.406001 | 27.983533 / 30.266828 / 41.618056 | 186.192047 / 190.957032 / 209.451929 | 188.300504 / 203.817561 / 265.421741 | -| Дверь в комнату Арто (arto-room-door) | 0.063265 / 0.073941 / 0.146470 | 0.483347 / 0.533563 / 0.561044 | 2.122744 / 2.239990 / 2.410086 | 4.410926 / 4.537777 / 6.422824 | -| Сундук у дальней кровати Арто (arto-far-bed-chest) | 0.199440 / 0.212210 / 0.331679 | 13.547542 / 13.620272 / 17.535142 | 78.977815 / 79.816627 / 82.365402 | 26.546390 / 26.896837 / 28.641021 | -| Сундук у ближней кровати Арто (arto-near-bed-chest) | 0.501715 / 0.520293 / 0.635606 | 14.866877 / 15.097875 / 16.566135 | 87.328599 / 87.605125 / 92.911028 | 69.048957 / 72.456504 / 82.421887 | -| Дверь на этаже спален покоев (gomez-bedrooms-door) | 5.449188 / 5.570851 / 6.123373 | 16.233054 / 16.523340 / 16.799676 | 92.039477 / 92.593561 / 92.853461 | 35.486030 / 36.058741 / 38.373735 | -| Сундук у дальней кровати (этаж спален) (gomez-bedrooms-far-chest) | 1.495080 / 1.962974 / 3.205933 | 2.072988 / 2.326961 / 3.976503 | 10.662161 / 11.987535 / 16.185301 | 5.977914 / 6.535844 / 8.364805 | -| Сундук у ближней кровати (этаж спален) (gomez-bedrooms-near-chest) | 0.117467 / 0.145089 / 0.159089 | 11.567103 / 11.670178 / 13.297066 | 65.116330 / 67.716052 / 95.263267 | 38.933377 / 41.407109 / 46.798349 | -| Левый сундук в башне Ворона (raven-tower-left-chest) | 1.332885 / 1.474369 / 2.742176 | 15.653531 / 16.923682 / 19.095039 | 94.090833 / 94.919509 / 95.294274 | 45.368709 / 47.149768 / 51.969109 | -| Правый сундук в башне Ворона (raven-tower-right-chest) | 0.144518 / 0.151548 / 0.180031 | 5.558523 / 6.447561 / 7.085630 | 31.400143 / 35.750863 / 38.894498 | 15.234651 / 15.675303 / 17.539329 | -| Сундук у лестницы (2-й этаж башни) (raven-tower-floor2-stairs-chest) | 4.273090 / 4.441643 / 7.479363 | 13.366460 / 13.868456 / 14.569059 | 80.161436 / 84.439873 / 86.146727 | 51.243917 / 52.779582 / 64.885643 | -| Второй сундук на 2-м этаже башни (raven-tower-floor2-chest-2) | 0.175905 / 0.199640 / 0.234653 | 2.012869 / 2.088602 / 2.346571 | 9.323398 / 9.793506 / 10.253748 | 2.983339 / 3.196580 / 3.440537 | -| Сундук у входа (3-й этаж башни) (raven-tower-floor3-entrance-chest) | 0.637650 / 0.707324 / 0.769047 | 1.540648 / 1.640669 / 3.248508 | 7.454707 / 8.776225 / 12.391845 | 6.350916 / 6.698889 / 9.026892 | -| Пустой сундук (3-й этаж башни) (raven-tower-floor3-empty-chest) | 0.070116 / 0.089505 / 0.095594 | 1.448309 / 1.461428 / 1.837645 | 8.154039 / 8.421802 / 8.643385 | 5.290109 / 5.552194 / 6.112657 | -| Сундук с эликсирами (4-й этаж башни) (raven-tower-floor4-elixirs-chest) | 0.109254 / 0.112309 / 0.136195 | 11.040161 / 11.221093 / 12.678586 | 73.129320 / 75.167478 / 82.039156 | 107.461847 / 112.569413 / 114.667008 | -| Сундук с едой (4-й этаж башни) (raven-tower-floor4-food-chest) | 0.267202 / 0.275865 / 0.295515 | 15.084486 / 15.373972 / 15.722726 | 90.678285 / 96.314105 / 109.625183 | 51.325845 / 52.314011 / 71.146988 | -| Сундук на вершине башни Ворона (raven-tower-top-chest) | 7.054034 / 7.251361 / 7.770963 | 11.989059 / 12.636073 / 14.032354 | 78.873203 / 82.082342 / 83.192121 | 43.022872 / 43.437415 / 48.328646 | -| Ящик у огненного ящера (fire-lizard-cave-chest) | 0.171709 / 0.175704 / 0.258559 | 10.301790 / 10.657055 / 11.986535 | 63.810299 / 64.970214 / 67.822405 | 53.291162 / 55.654889 / 69.187811 | -| Сундук в подвале таверны (silas-tavern-basement-chest) | 0.086651 / 0.114051 / 0.125208 | 1.361138 / 1.583773 / 2.494533 | 6.591978 / 7.191901 / 9.724732 | 3.855982 / 3.980659 / 4.069674 | -| Сундук склада таверны (silas-tavern-storeroom-chest) | 0.103596 / 0.105509 / 0.130747 | 13.265009 / 13.681646 / 15.403706 | 86.416723 / 90.603633 / 94.456980 | 30.445969 / 30.779861 / 31.124511 | -| Склад таверны - дверь (silas-tavern-storeroom-door) | 0.170056 / 0.175664 / 0.191708 | 13.175474 / 13.437288 / 14.941331 | 75.461670 / 81.038310 / 83.869430 | 22.194804 / 22.875538 / 24.601695 | -| Сундук в домике напротив Джана (jan-pond-house-chest) | 0.098569 / 0.103966 / 0.105930 | 6.958811 / 7.156618 / 8.019226 | 44.521017 / 45.811578 / 47.468221 | 47.224825 / 47.618628 / 48.630679 | -| Сундук Грэхэма (graham-chest) | 0.105608 / 0.109424 / 0.138429 | 16.359714 / 17.144644 / 18.131960 | 99.469936 / 101.199529 / 103.562794 | 30.639528 / 33.141643 / 36.963954 | -| Сундук Граво (gravo-chest) | 0.061162 / 0.071658 / 0.113180 | 1.004722 / 1.107086 / 1.167787 | 5.293044 / 5.463670 / 5.936682 | 5.050748 / 5.359533 / 5.448868 | -| Сундук Диего (diego-chest) | 0.159910 / 0.162244 / 0.215634 | 12.245143 / 12.377212 / 13.043719 | 79.944612 / 82.868842 / 86.734019 | 28.887343 / 29.897692 / 32.130710 | -| Сундук Грима (grim-chest) | 0.199200 / 0.213581 / 0.528836 | 16.700641 / 16.907052 / 18.926581 | 100.218671 / 106.557810 / 116.943629 | 19.493485 / 20.138955 / 20.343854 | -| Сундук наёмника (у Грима) (mercenary-near-grim-chest) | 0.091758 / 0.098238 / 0.118909 | 15.047443 / 15.120403 / 15.504441 | 93.219770 / 96.119853 / 100.796931 | 28.609512 / 29.191117 / 34.457432 | -| Популярный замок (popular-lock) | 0.550629 / 0.571239 / 0.592822 | 1.910796 / 1.922192 / 1.952307 | 9.808651 / 9.924765 / 10.116053 | 5.526196 / 5.561629 / 5.680868 | -| Сундук в хижине шахты Альберто (alberto-mine-hut-chest) | 0.059810 / 0.065548 / 0.097607 | 1.766468 / 1.777285 / 1.806328 | 8.646632 / 9.241657 / 10.841905 | 5.200596 / 5.284802 / 5.329430 | -| Левый сундук наверху в шахте Альберто (alberto-mine-upstairs-left-chest) | 0.015704 / 0.018408 / 0.030526 | 0.013030 / 0.014802 / 0.015343 | 0.034912 / 0.038548 / 0.046410 | 0.198168 / 0.210096 / 0.235705 | -| Правый сундук наверху в шахте Альберто (alberto-mine-upstairs-right-chest) | 0.026289 / 0.032729 / 0.050536 | 0.496917 / 0.510779 / 0.644560 | 2.125088 / 2.144357 / 2.693213 | 1.517594 / 1.549391 / 1.567980 | -| Дверь на склад охотника Кавалорна (cavalorn-storeroom-door) | 0.236295 / 0.238709 / 0.242344 | 13.715700 / 13.902150 / 14.243180 | 79.491600 / 81.640773 / 90.801756 | 40.194055 / 40.408977 / 42.256058 | -| Сундук у разбойников на пути в Новый лагерь (old-to-new-camp-bandits-chest) | 0.411970 / 0.431309 / 0.438249 | 9.617881 / 9.699493 / 11.340953 | 61.399960 / 66.138349 / 66.895689 | 24.716668 / 25.806637 / 27.208237 | - -## Correctness checks - -- Every warmup and measured solution independently replayed one division at a time. -- Matrix A*, exact BFS, and original Apache BFS equal the stored exact action minimum. -- Current BFS preserves the pinned original BFS command sequence. -- Inputs are unchanged and every implementation returns a deterministic path. -- Live reference path and A/U/C equal the cached pinned observation. - -## Limits of the conclusions - -- These are 45 fixed catalog configurations, not a random sample or a claim about every game state. -- Only actions (A) are minimized by the production solver; distinct plates (U) and unit divisions (C) are descriptive metrics. -- The reference minimizes unit divisions before plate switches; its objective differs from minimum grouped actions. -- Timing reflects this runtime, machine, sample count, JIT and garbage collection; no timing threshold is a correctness gate. -- These are Node.js timings, not measurements in an actual browser or Web Worker. -- Cached historical timings are provenance only and are excluded from current speed comparisons. -- The sum of per-lock medians is an aggregate of medians, not the median runtime of a whole-catalog pass. -- Speedup ratios above 1 favor matrix A*; ratios below 1 favor the comparator. Timing ties require equal measured medians. - -The JSON report includes individual samples and independently verified command sequences. Cached observations never supply values to the live timing table. diff --git a/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark-evidence.json b/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark-evidence.json deleted file mode 100644 index f693304..0000000 --- a/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark-evidence.json +++ /dev/null @@ -1,5953 +0,0 @@ -{ - "schemaVersion": 1, - "createdAt": "2026-09-07T09:12:21.367Z", - "quality": "passed", - "performance": "inconclusive", - "qualitySnapshot": { - "schemaVersion": 1, - "fixtureSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", - "source": { - "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", - "moduleSha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b", - "format": "tuple", - "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/dist/gothic-lock-solver.mjs" - }, - "reason": "Measured candidate paths and quality in this immutable benchmark evidence.", - "locks": [ - { - "id": "ruined-tower-door", - "optimumA": 17, - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - [ - 0, - -4 - ], - [ - 2, - 5 - ], - [ - 3, - -6 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 5, - -2 - ], - [ - 0, - -4 - ], - [ - 1, - 3 - ], - [ - 3, - -5 - ], - [ - 4, - -3 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 1, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "scatty-chest", - "optimumA": 9, - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - 5 - ], - [ - 4, - 5 - ], - [ - 1, - -1 - ], - [ - 3, - 5 - ], - [ - 4, - 2 - ], - [ - 0, - -2 - ], - [ - 2, - 2 - ], - [ - 4, - 6 - ], - [ - 1, - -3 - ] - ] - }, - { - "id": "cor-galom-chest", - "optimumA": 5, - "A": 5, - "U": 4, - "C": 15, - "plateSwitches": 4, - "commands": [ - [ - 3, - 4 - ], - [ - 1, - -4 - ], - [ - 0, - 4 - ], - [ - 1, - -1 - ], - [ - 2, - -2 - ] - ] - }, - { - "id": "cor-galom-bedroom-chest", - "optimumA": 14, - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - [ - 1, - -5 - ], - [ - 2, - -4 - ], - [ - 1, - -4 - ], - [ - 4, - -4 - ], - [ - 1, - -4 - ], - [ - 3, - 4 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 2, - -2 - ], - [ - 1, - -6 - ], - [ - 4, - -6 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "swamp-camp-forge-chest", - "optimumA": 11, - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 2, - 6 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 3, - -5 - ], - [ - 2, - 5 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 1, - -1 - ], - [ - 2, - 5 - ], - [ - 3, - -3 - ], - [ - 4, - -2 - ] - ] - }, - { - "id": "rice-lord-storeroom", - "optimumA": 9, - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 1 - ], - [ - 4, - 3 - ], - [ - 2, - 4 - ], - [ - 1, - 2 - ], - [ - 0, - 3 - ], - [ - 3, - 5 - ], - [ - 4, - 3 - ], - [ - 1, - 6 - ], - [ - 0, - 3 - ] - ] - }, - { - "id": "gomez-chambers-door", - "optimumA": 9, - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - [ - 3, - -3 - ], - [ - 2, - 2 - ], - [ - 1, - -2 - ], - [ - 0, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -4 - ], - [ - 5, - -4 - ], - [ - 3, - -5 - ], - [ - 1, - -3 - ] - ] - }, - { - "id": "gomez-throne-hall-chest", - "optimumA": 5, - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - [ - 3, - 6 - ], - [ - 0, - 6 - ], - [ - 1, - 2 - ], - [ - 2, - 1 - ], - [ - 3, - 1 - ] - ] - }, - { - "id": "old-camp-dungeon-door", - "optimumA": 17, - "A": 17, - "U": 6, - "C": 72, - "plateSwitches": 16, - "commands": [ - [ - 4, - -2 - ], - [ - 1, - -1 - ], - [ - 3, - 2 - ], - [ - 2, - -4 - ], - [ - 5, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - -5 - ], - [ - 5, - -6 - ], - [ - 2, - -6 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 1, - -6 - ], - [ - 0, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ], - [ - 5, - -6 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "arlin-dungeon-key", - "optimumA": 7, - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - [ - 2, - -2 - ], - [ - 5, - -2 - ], - [ - 6, - -6 - ], - [ - 0, - -3 - ], - [ - 2, - -6 - ], - [ - 1, - 3 - ], - [ - 5, - -3 - ] - ] - }, - { - "id": "gomez-wine-cellar", - "optimumA": 9, - "A": 9, - "U": 6, - "C": 41, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 2, - 5 - ], - [ - 5, - 4 - ], - [ - 3, - 4 - ], - [ - 2, - -6 - ], - [ - 4, - -5 - ], - [ - 3, - 6 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ] - ] - }, - { - "id": "gomez-bedside-chest", - "optimumA": 23, - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - [ - 2, - -1 - ], - [ - 1, - -1 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -4 - ], - [ - 0, - -4 - ], - [ - 1, - -4 - ], - [ - 4, - 2 - ], - [ - 2, - -2 - ], - [ - 3, - -5 - ], - [ - 0, - -4 - ], - [ - 1, - -6 - ], - [ - 4, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - 3 - ], - [ - 1, - -6 - ], - [ - 0, - -3 - ] - ] - }, - { - "id": "gomez-wall-chest", - "optimumA": 20, - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - [ - 0, - -2 - ], - [ - 4, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ], - [ - 5, - -6 - ], - [ - 3, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 3, - -3 - ], - [ - 5, - -5 - ], - [ - 0, - -2 - ], - [ - 5, - -2 - ], - [ - 2, - 4 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ], - [ - 1, - -3 - ] - ] - }, - { - "id": "voran-bedside-chest", - "optimumA": 9, - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 4, - -1 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 3, - 6 - ], - [ - 5, - 6 - ], - [ - 3, - 6 - ], - [ - 6, - -5 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "arto-room-door", - "optimumA": 7, - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - [ - 1, - -6 - ], - [ - 0, - -6 - ], - [ - 1, - -1 - ], - [ - 3, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - 5 - ], - [ - 2, - -3 - ] - ] - }, - { - "id": "arto-far-bed-chest", - "optimumA": 11, - "A": 11, - "U": 5, - "C": 44, - "plateSwitches": 10, - "commands": [ - [ - 1, - 4 - ], - [ - 2, - -2 - ], - [ - 5, - -2 - ], - [ - 3, - 3 - ], - [ - 4, - 6 - ], - [ - 1, - 6 - ], - [ - 5, - -6 - ], - [ - 4, - 6 - ], - [ - 1, - 3 - ], - [ - 2, - 3 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "arto-near-bed-chest", - "optimumA": 13, - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - [ - 4, - -1 - ], - [ - 0, - 6 - ], - [ - 5, - -3 - ], - [ - 3, - 4 - ], - [ - 2, - 6 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 3, - 4 - ], - [ - 0, - 6 - ], - [ - 2, - 4 - ], - [ - 5, - -6 - ], - [ - 1, - -3 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "gomez-bedrooms-door", - "optimumA": 14, - "A": 14, - "U": 6, - "C": 49, - "plateSwitches": 13, - "commands": [ - [ - 3, - -6 - ], - [ - 5, - -2 - ], - [ - 2, - 2 - ], - [ - 0, - 3 - ], - [ - 1, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - 3 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 5, - 3 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ] - ] - }, - { - "id": "gomez-bedrooms-far-chest", - "optimumA": 14, - "A": 14, - "U": 5, - "C": 43, - "plateSwitches": 13, - "commands": [ - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 3, - 2 - ], - [ - 4, - -3 - ], - [ - 2, - -4 - ], - [ - 0, - -5 - ], - [ - 1, - -4 - ], - [ - 0, - -2 - ], - [ - 3, - 2 - ], - [ - 2, - -3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "gomez-bedrooms-near-chest", - "optimumA": 12, - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - [ - 0, - 4 - ], - [ - 2, - 1 - ], - [ - 5, - -6 - ], - [ - 4, - -6 - ], - [ - 0, - 6 - ], - [ - 5, - -6 - ], - [ - 1, - -6 - ], - [ - 0, - 6 - ], - [ - 3, - -3 - ], - [ - 0, - 3 - ], - [ - 5, - -6 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "raven-tower-left-chest", - "optimumA": 11, - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - [ - 4, - 2 - ], - [ - 5, - -3 - ], - [ - 3, - 2 - ], - [ - 0, - 5 - ], - [ - 2, - -4 - ], - [ - 4, - 5 - ], - [ - 5, - -1 - ], - [ - 1, - -3 - ], - [ - 3, - 5 - ], - [ - 0, - 3 - ], - [ - 1, - -2 - ] - ] - }, - { - "id": "raven-tower-right-chest", - "optimumA": 8, - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - [ - 1, - 5 - ], - [ - 4, - 1 - ], - [ - 5, - -6 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -2 - ], - [ - 3, - 2 - ], - [ - 5, - -1 - ] - ] - }, - { - "id": "raven-tower-floor2-stairs-chest", - "optimumA": 16, - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - [ - 5, - 3 - ], - [ - 0, - -1 - ], - [ - 4, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - 4 - ], - [ - 3, - -2 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 1, - 6 - ], - [ - 2, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ] - ] - }, - { - "id": "raven-tower-floor2-chest-2", - "optimumA": 8, - "A": 8, - "U": 5, - "C": 29, - "plateSwitches": 7, - "commands": [ - [ - 3, - 1 - ], - [ - 2, - -5 - ], - [ - 4, - 5 - ], - [ - 3, - -3 - ], - [ - 0, - -1 - ], - [ - 1, - 5 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ] - ] - }, - { - "id": "raven-tower-floor3-entrance-chest", - "optimumA": 14, - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - [ - 3, - -4 - ], - [ - 1, - 1 - ], - [ - 4, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - 2 - ], - [ - 4, - -4 - ], - [ - 2, - -4 - ], - [ - 1, - 3 - ], - [ - 3, - -6 - ], - [ - 4, - -3 - ], - [ - 0, - -3 - ], - [ - 1, - 3 - ], - [ - 4, - -5 - ], - [ - 0, - -2 - ] - ] - }, - { - "id": "raven-tower-floor3-empty-chest", - "optimumA": 9, - "A": 9, - "U": 5, - "C": 37, - "plateSwitches": 8, - "commands": [ - [ - 0, - -2 - ], - [ - 4, - 6 - ], - [ - 2, - 3 - ], - [ - 1, - -3 - ], - [ - 4, - 6 - ], - [ - 3, - -4 - ], - [ - 0, - -6 - ], - [ - 4, - 4 - ], - [ - 2, - 3 - ] - ] - }, - { - "id": "raven-tower-floor4-elixirs-chest", - "optimumA": 8, - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - [ - 1, - -5 - ], - [ - 2, - -5 - ], - [ - 1, - -5 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ], - [ - 2, - -6 - ], - [ - 1, - -3 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "raven-tower-floor4-food-chest", - "optimumA": 11, - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - [ - 2, - 4 - ], - [ - 0, - 4 - ], - [ - 2, - 4 - ], - [ - 4, - 6 - ], - [ - 0, - 5 - ], - [ - 3, - -1 - ], - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - 2 - ], - [ - 5, - -3 - ] - ] - }, - { - "id": "raven-tower-top-chest", - "optimumA": 20, - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - [ - 1, - -3 - ], - [ - 3, - 1 - ], - [ - 0, - -1 - ], - [ - 4, - -2 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 3, - 2 - ], - [ - 5, - -4 - ], - [ - 2, - -1 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - -6 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "fire-lizard-cave-chest", - "optimumA": 10, - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - [ - 0, - -3 - ], - [ - 2, - -5 - ], - [ - 4, - 4 - ], - [ - 1, - 3 - ], - [ - 5, - -5 - ], - [ - 0, - -6 - ], - [ - 3, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - 6 - ], - [ - 0, - -3 - ] - ] - }, - { - "id": "silas-tavern-basement-chest", - "optimumA": 9, - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 2 - ], - [ - 4, - 2 - ], - [ - 2, - 4 - ], - [ - 3, - -3 - ], - [ - 1, - -4 - ], - [ - 3, - -3 - ], - [ - 4, - 6 - ], - [ - 2, - 3 - ], - [ - 3, - -3 - ] - ] - }, - { - "id": "silas-tavern-storeroom-chest", - "optimumA": 9, - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - -3 - ], - [ - 3, - 4 - ], - [ - 5, - -6 - ], - [ - 0, - 2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -6 - ], - [ - 3, - 3 - ], - [ - 5, - -3 - ] - ] - }, - { - "id": "silas-tavern-storeroom-door", - "optimumA": 10, - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - [ - 3, - 2 - ], - [ - 5, - -3 - ], - [ - 1, - -3 - ], - [ - 5, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - 3 - ], - [ - 5, - -3 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 2, - -3 - ] - ] - }, - { - "id": "jan-pond-house-chest", - "optimumA": 8, - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - [ - 0, - -4 - ], - [ - 2, - -6 - ], - [ - 3, - 4 - ], - [ - 4, - 3 - ], - [ - 5, - -3 - ], - [ - 3, - 5 - ], - [ - 5, - -5 - ], - [ - 1, - 3 - ] - ] - }, - { - "id": "graham-chest", - "optimumA": 10, - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - [ - 1, - 5 - ], - [ - 4, - 6 - ], - [ - 0, - -5 - ], - [ - 1, - 6 - ], - [ - 4, - 5 - ], - [ - 1, - 5 - ], - [ - 2, - 2 - ], - [ - 3, - -3 - ], - [ - 4, - 2 - ], - [ - 5, - -1 - ] - ] - }, - { - "id": "gravo-chest", - "optimumA": 8, - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - [ - 0, - -5 - ], - [ - 4, - -1 - ], - [ - 1, - 3 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 3, - 3 - ], - [ - 4, - -6 - ], - [ - 1, - 3 - ] - ] - }, - { - "id": "diego-chest", - "optimumA": 11, - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - [ - 0, - 2 - ], - [ - 1, - 1 - ], - [ - 2, - -6 - ], - [ - 3, - -1 - ], - [ - 1, - 3 - ], - [ - 4, - 3 - ], - [ - 2, - -6 - ], - [ - 5, - 3 - ], - [ - 1, - 6 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ] - ] - }, - { - "id": "grim-chest", - "optimumA": 8, - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - [ - 4, - -2 - ], - [ - 3, - 1 - ], - [ - 1, - 6 - ], - [ - 2, - -1 - ], - [ - 0, - -5 - ], - [ - 3, - 4 - ], - [ - 0, - -1 - ], - [ - 5, - -1 - ] - ] - }, - { - "id": "mercenary-near-grim-chest", - "optimumA": 8, - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - [ - 2, - 3 - ], - [ - 1, - 3 - ], - [ - 3, - -6 - ], - [ - 5, - 6 - ], - [ - 0, - -2 - ], - [ - 3, - -2 - ], - [ - 4, - 2 - ], - [ - 5, - 1 - ] - ] - }, - { - "id": "popular-lock", - "optimumA": 13, - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - [ - 4, - -4 - ], - [ - 2, - -1 - ], - [ - 3, - -2 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - 6 - ], - [ - 2, - -3 - ], - [ - 4, - -6 - ], - [ - 1, - -3 - ], - [ - 0, - 3 - ], - [ - 3, - -3 - ], - [ - 4, - -6 - ], - [ - 0, - 3 - ] - ] - }, - { - "id": "alberto-mine-hut-chest", - "optimumA": 9, - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - [ - 3, - -4 - ], - [ - 1, - -2 - ], - [ - 4, - 6 - ], - [ - 0, - -3 - ], - [ - 3, - -6 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - 3 - ] - ] - }, - { - "id": "alberto-mine-upstairs-left-chest", - "optimumA": 4, - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 1, - 6 - ], - [ - 3, - 3 - ] - ] - }, - { - "id": "alberto-mine-upstairs-right-chest", - "optimumA": 5, - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - [ - 0, - 3 - ], - [ - 1, - 2 - ], - [ - 2, - 2 - ], - [ - 4, - 4 - ], - [ - 3, - 2 - ] - ] - }, - { - "id": "cavalorn-storeroom-door", - "optimumA": 11, - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 3, - -2 - ], - [ - 5, - 4 - ], - [ - 0, - -1 - ], - [ - 1, - -6 - ], - [ - 0, - -4 - ], - [ - 3, - -2 - ], - [ - 1, - -6 - ], - [ - 4, - -4 - ], - [ - 5, - 6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ] - ] - }, - { - "id": "old-to-new-camp-bandits-chest", - "optimumA": 10, - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - [ - 4, - 6 - ], - [ - 5, - -2 - ], - [ - 2, - -1 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ] - ] - } - ] - }, - "metadata": { - "environment": { - "node": "v24.19.0", - "v8": "13.6.233.17-node.51", - "platform": "linux", - "arch": "x64", - "cpu": "AMD EPYC 9V74 80-Core Processor", - "logicalCpus": 9, - "osRelease": "6.18.35", - "totalMemoryBytes": 23109910528, - "execArgv": [] - }, - "source": { - "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", - "dirty": false, - "files": { - "errors.ts": "830e0995bb1d5b98e8ce0495a22e5ae169cab10b2bdff2612e58323c397306f5", - "index.ts": "a1a2eedf5010cd66fc5d451035923e6ab417b36ba84cb2da85789cea17a1569a", - "indexed-heap.ts": "f776755f7405968b567922473c1e6ea5a63dae7643c4bd62b993159e900b7fbc", - "indexed.ts": "279b11d625a033f45723dd94df13a9be9fdfe267cff2535d5470b2011f11e125", - "lock-model.ts": "b4674921412e2acfbf0eaeff2031b3fc3907dffe886c42e588cd5ba6348ec966", - "matrix-analysis.ts": "a105dee650d645a730e3cc7e62a8e9faae31b29ad98c28aafce5ef15df236fc6", - "matrix-search.ts": "380ae745289a7ffd1997a6b9aa7dc3dd06a56e17453732d41042891c42435d3a", - "search-bfs.ts": "011c675973ccc8d753c0b9b3608882b16ca93ef4b460635c3474d8d5fc705feb", - "search-limits.ts": "91afc0940d49af46d9bb57d91af45df992f0684593d2f2d8bc3c13fad3f4c681", - "types.ts": "a2e72cf02e31694c52c1621cf5359863585b8383aa83a7d4e977c8547e8c879b" - }, - "harness": { - "catalog.ts": "2b75c9e9b5fe6997006307bf74cd90d8645d6d61fc527681f209c164680b880a", - "cli.ts": "b717c3d69c13d4a5b9a77589b1465f96338fe9d8be6501249f4470db035efe33", - "comparison.ts": "680ac76d266b51ff1f951fd3e232248bc5df0ab9eb70123285d31d176fb3a709", - "harness.ts": "14d23d697b2c4dd8adc13beac84b1cd3c0236aa34ae42ea96935fb30ac75e9f8", - "memory.ts": "429b45f541a2ac97e46fb6598ac30966cab4e2dca7e355c56f3ad3e037f43d93", - "rss.ts": "acc321bc34deac44819e5ff6d9af89e91c7394d9d897f7d69ce0cfedfc2e992c", - "solver.bench.ts": "985fbba3b387109261abeb1fdcbecd91b0ccfaa4dde5e0f162ad1758d529647f", - "validation.ts": "c44fc2a3de4da26ab207186a97a6b80ab20a910d27ae335da6cec536e402d77d" - }, - "scripts": { - "benchmark-compare.ts": "d4243c84a0a007b611e04e9c07031c3a7e48fb1a610366349084f0a7ab61cd71", - "benchmark-memory-worker.ts": "807707ca177f43c5ef08b88aa1068ad5f08f391b74f7f8d358159a2e5bf8a49b", - "benchmark-memory.ts": "c25a0407122f1c16261d8f8f9fd6f60cba463a7e288579ba67fde398db8d854f", - "benchmark-snapshot.ts": "50b31ffeac808f5b21f0fb864d2efd8a7c758491cf4e2d611bbe900bf3f9ec32", - "benchmark.ts": "fdb3f22dfdd495a6dde6a27fc3b0db68c7ceb75b94acaff131d30a25b6a06f24" - } - }, - "candidate": { - "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", - "moduleSha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b", - "format": "tuple", - "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/dist/gothic-lock-solver.mjs" - }, - "baseline": { - "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", - "moduleSha256": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "format": "legacy", - "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/reference-matrix/src/index.mjs", - "moduleTree": { - "cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", - "index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", - "matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", - "matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", - "result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", - "search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", - "solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", - "state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" - }, - "snapshotSha256": "c21b6319523f625439403c0417d267b5a63c35abafebef758bfc6c21217a3b94", - "snapshot": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/snapshots/reference-matrix.json", - "kind": "reference-baseline" - }, - "fixtures": { - "count": 45, - "catalogSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", - "manifestSha256": "a634d5328fd8d41470397b622ae0bd32b8aac0355dfc31975e023a63a62e65c9" - }, - "originalBfs": { - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "a87e739a22ccc4215d6d9b14fa06b0738171cb60", - "license": "Apache-2.0", - "files": { - "src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", - "src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", - "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", - "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", - "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" - }, - "notes": "Unmodified source snapshots; imports remain relative within this directory. No upstream UnlockMyLoot solver code is included." - }, - "matrixReference": { - "schemaVersion": 1, - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", - "license": "Apache-2.0", - "files": { - "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", - "src/cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", - "src/index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "src/lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", - "src/matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", - "src/matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", - "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "src/search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", - "src/search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", - "src/solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", - "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" - }, - "notes": "Byte-identical Apache matrix reference, copied from source at the pinned revision. Benchmark-only; excluded from runtime and npm package. No external AGPL solver source." - }, - "upstream": { - "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "solver": { - "path": "index.html", - "sha256": "691f744099a9169f30e9883fddc5fdbb4949d306f3011e601678419d7b93821b", - "blockSha256": "d5bc85f40fd7c5b61097403df399ecb7dfd2b58924dc7a670aa0d05306017380", - "startMarker": "/* solver-start", - "endMarker": "/* solver-end */" - }, - "measured": true, - "externalCheckout": "/workspace/scratch/5d94b8672fa4/unlockmyloot", - "license": "External AGPL source is hash-verified and executed only from the supplied pinned checkout; no upstream solver code is vendored." - }, - "artifacts": { - "gothic-lock-solver.mjs": { - "bytes": 20143, - "sha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b" - }, - "gothic-lock-solver.min.mjs": { - "bytes": 9098, - "sha256": "9a7c0d365a0e2630372200f4c8a7914024b04afd048ca77e6a6783c120676d1f" - }, - "gothic-lock-solver.js": { - "bytes": 20916, - "sha256": "14841c4bc1563f4fd8beae3e277798878a2862efa0840e50b1aef9d5e5dff101" - }, - "gothic-lock-solver.min.js": { - "bytes": 9197, - "sha256": "2a936f190ae38f458e2b209681beb099974f32d654eb33be7249be6ede847694" - }, - "gothic-lock-solver.cli.mjs": { - "bytes": 23199, - "sha256": "75129dff219af243b05ffcbdb63ac1a14a32fd0d0b5ae8ee4528bb4d24b75f81" - } - } - }, - "memory": { - "metric": "Linux /proc/self/status VmHWM × 1024 bytes (current process-image RSS high-water)", - "repetitions": 5, - "sources": [ - "https://docs.kernel.org/filesystems/proc.html", - "https://nodejs.org/api/process.html#processresourceusage" - ], - "accuracy": "Kernel RSS accounting is asynchronous and approximate; this is not an exact allocation count. Raw getrusage maxRSS and startup/current-image peaks are preserved to expose pre-exec launch floors.", - "scope": "Each per-lock observation is a fresh process. Repeated comparison is peak RSS of the selected 45-lock workload per fresh process.", - "includes": "Node runtime, TypeScript benchmark loader, module loading, fixture loading, normalization, independent replay and solver allocations. Startup and loaded current RSS are recorded separately; subtraction is not exact algorithm allocation.", - "browser": { - "available": false, - "reason": "No portable exact browser peak-memory API; Node RSS is not browser memory." - }, - "performance": { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.962246756993597, - "noiseRatio": 1.0949647700001646, - "lowerRatio": 0.8908822461111241, - "upperRatio": 1.0393279531458977 - }, - "attempts": [ - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.962246756993597, - "noiseRatio": 1.0949647700001646, - "lowerRatio": 0.8908822461111241, - "upperRatio": 1.0393279531458977, - "samples": { - "candidate": [ - { - "peakRssBytes": 94666752, - "startupRssBytes": 64344064, - "loadedRssBytes": 65785856, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65351680, - "loadedPeakRssBytes": 66846720, - "resourceUsageMaxRssBytes": 93732864, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94412800, - "startupRssBytes": 65175552, - "loadedRssBytes": 66617344, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66248704, - "loadedPeakRssBytes": 67477504, - "resourceUsageMaxRssBytes": 93741056, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94752768, - "startupRssBytes": 65814528, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66715648, - "loadedPeakRssBytes": 67567616, - "resourceUsageMaxRssBytes": 94523392, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89186304, - "startupRssBytes": 65310720, - "loadedRssBytes": 66883584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66576384, - "loadedPeakRssBytes": 67739648, - "resourceUsageMaxRssBytes": 89006080, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84447232, - "startupRssBytes": 65482752, - "loadedRssBytes": 67186688, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66744320, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "baseline": [ - { - "peakRssBytes": 95948800, - "startupRssBytes": 64970752, - "loadedRssBytes": 67461120, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66179072, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 95100928, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89391104, - "startupRssBytes": 64778240, - "loadedRssBytes": 67137536, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65925120, - "loadedPeakRssBytes": 68210688, - "resourceUsageMaxRssBytes": 88641536, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 100007936, - "startupRssBytes": 64532480, - "loadedRssBytes": 67678208, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65564672, - "loadedPeakRssBytes": 68284416, - "resourceUsageMaxRssBytes": 100007936, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95895552, - "startupRssBytes": 65687552, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67055616, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 95895552, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 93995008, - "startupRssBytes": 65671168, - "loadedRssBytes": 67375104, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66916352, - "loadedPeakRssBytes": 68136960, - "resourceUsageMaxRssBytes": 93188096, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "originalBfs": [ - { - "peakRssBytes": 121143296, - "startupRssBytes": 65421312, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66490368, - "loadedPeakRssBytes": 67485696, - "resourceUsageMaxRssBytes": 120414208, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120922112, - "startupRssBytes": 65372160, - "loadedRssBytes": 67207168, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 120160256, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120438784, - "startupRssBytes": 64557056, - "loadedRssBytes": 66392064, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65339392, - "loadedPeakRssBytes": 67448832, - "resourceUsageMaxRssBytes": 119697408, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120311808, - "startupRssBytes": 65306624, - "loadedRssBytes": 67141632, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66830336, - "loadedPeakRssBytes": 68071424, - "resourceUsageMaxRssBytes": 119595008, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 121356288, - "startupRssBytes": 65691648, - "loadedRssBytes": 67395584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 68116480, - "resourceUsageMaxRssBytes": 120606720, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "upstream": [ - { - "peakRssBytes": 270356480, - "startupRssBytes": 65941504, - "loadedRssBytes": 68431872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67108864, - "loadedPeakRssBytes": 69251072, - "resourceUsageMaxRssBytes": 269815808, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270110720, - "startupRssBytes": 65540096, - "loadedRssBytes": 68685824, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 69443584, - "resourceUsageMaxRssBytes": 269246464, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 268992512, - "startupRssBytes": 64909312, - "loadedRssBytes": 68710400, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65966080, - "loadedPeakRssBytes": 69349376, - "resourceUsageMaxRssBytes": 267882496, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 309043200, - "startupRssBytes": 65306624, - "loadedRssBytes": 68452352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66768896, - "loadedPeakRssBytes": 69234688, - "resourceUsageMaxRssBytes": 309043200, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270135296, - "startupRssBytes": 65089536, - "loadedRssBytes": 68497408, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65785856, - "loadedPeakRssBytes": 69300224, - "resourceUsageMaxRssBytes": 269443072, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "baselineSelfA": [ - { - "peakRssBytes": 95563776, - "startupRssBytes": 65470464, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66633728, - "loadedPeakRssBytes": 68255744, - "resourceUsageMaxRssBytes": 95563776, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95862784, - "startupRssBytes": 65462272, - "loadedRssBytes": 67297280, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66928640, - "loadedPeakRssBytes": 68186112, - "resourceUsageMaxRssBytes": 95862784, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94965760, - "startupRssBytes": 65818624, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67014656, - "loadedPeakRssBytes": 68362240, - "resourceUsageMaxRssBytes": 94965760, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95129600, - "startupRssBytes": 65208320, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66326528, - "loadedPeakRssBytes": 68149248, - "resourceUsageMaxRssBytes": 94162944, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94371840, - "startupRssBytes": 64974848, - "loadedRssBytes": 66940928, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66097152, - "loadedPeakRssBytes": 67719168, - "resourceUsageMaxRssBytes": 94371840, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "baselineSelfB": [ - { - "peakRssBytes": 96808960, - "startupRssBytes": 64933888, - "loadedRssBytes": 67424256, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66068480, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 96731136, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94896128, - "startupRssBytes": 65703936, - "loadedRssBytes": 67407872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 94896128, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84250624, - "startupRssBytes": 65826816, - "loadedRssBytes": 67530752, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66949120, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 97107968, - "startupRssBytes": 65032192, - "loadedRssBytes": 66998272, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66183168, - "loadedPeakRssBytes": 67727360, - "resourceUsageMaxRssBytes": 97107968, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94167040, - "startupRssBytes": 64937984, - "loadedRssBytes": 67428352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66555904, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 93442048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ] - } - } - ], - "wholeCatalog": { - "candidate": { - "samples": [ - { - "peakRssBytes": 94666752, - "startupRssBytes": 64344064, - "loadedRssBytes": 65785856, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65351680, - "loadedPeakRssBytes": 66846720, - "resourceUsageMaxRssBytes": 93732864, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94412800, - "startupRssBytes": 65175552, - "loadedRssBytes": 66617344, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66248704, - "loadedPeakRssBytes": 67477504, - "resourceUsageMaxRssBytes": 93741056, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94752768, - "startupRssBytes": 65814528, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66715648, - "loadedPeakRssBytes": 67567616, - "resourceUsageMaxRssBytes": 94523392, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89186304, - "startupRssBytes": 65310720, - "loadedRssBytes": 66883584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66576384, - "loadedPeakRssBytes": 67739648, - "resourceUsageMaxRssBytes": 89006080, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84447232, - "startupRssBytes": 65482752, - "loadedRssBytes": 67186688, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66744320, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 84447232, - "median": 94412800, - "p95": 94752768, - "max": 94752768 - } - }, - "baseline": { - "samples": [ - { - "peakRssBytes": 95948800, - "startupRssBytes": 64970752, - "loadedRssBytes": 67461120, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66179072, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 95100928, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89391104, - "startupRssBytes": 64778240, - "loadedRssBytes": 67137536, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65925120, - "loadedPeakRssBytes": 68210688, - "resourceUsageMaxRssBytes": 88641536, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 100007936, - "startupRssBytes": 64532480, - "loadedRssBytes": 67678208, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65564672, - "loadedPeakRssBytes": 68284416, - "resourceUsageMaxRssBytes": 100007936, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95895552, - "startupRssBytes": 65687552, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67055616, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 95895552, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 93995008, - "startupRssBytes": 65671168, - "loadedRssBytes": 67375104, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66916352, - "loadedPeakRssBytes": 68136960, - "resourceUsageMaxRssBytes": 93188096, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 89391104, - "median": 95895552, - "p95": 100007936, - "max": 100007936 - } - }, - "originalBfs": { - "samples": [ - { - "peakRssBytes": 121143296, - "startupRssBytes": 65421312, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66490368, - "loadedPeakRssBytes": 67485696, - "resourceUsageMaxRssBytes": 120414208, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120922112, - "startupRssBytes": 65372160, - "loadedRssBytes": 67207168, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 120160256, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120438784, - "startupRssBytes": 64557056, - "loadedRssBytes": 66392064, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65339392, - "loadedPeakRssBytes": 67448832, - "resourceUsageMaxRssBytes": 119697408, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120311808, - "startupRssBytes": 65306624, - "loadedRssBytes": 67141632, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66830336, - "loadedPeakRssBytes": 68071424, - "resourceUsageMaxRssBytes": 119595008, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 121356288, - "startupRssBytes": 65691648, - "loadedRssBytes": 67395584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 68116480, - "resourceUsageMaxRssBytes": 120606720, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 120311808, - "median": 120922112, - "p95": 121356288, - "max": 121356288 - } - }, - "upstream": { - "samples": [ - { - "peakRssBytes": 270356480, - "startupRssBytes": 65941504, - "loadedRssBytes": 68431872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67108864, - "loadedPeakRssBytes": 69251072, - "resourceUsageMaxRssBytes": 269815808, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270110720, - "startupRssBytes": 65540096, - "loadedRssBytes": 68685824, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 69443584, - "resourceUsageMaxRssBytes": 269246464, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 268992512, - "startupRssBytes": 64909312, - "loadedRssBytes": 68710400, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65966080, - "loadedPeakRssBytes": 69349376, - "resourceUsageMaxRssBytes": 267882496, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 309043200, - "startupRssBytes": 65306624, - "loadedRssBytes": 68452352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66768896, - "loadedPeakRssBytes": 69234688, - "resourceUsageMaxRssBytes": 309043200, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270135296, - "startupRssBytes": 65089536, - "loadedRssBytes": 68497408, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65785856, - "loadedPeakRssBytes": 69300224, - "resourceUsageMaxRssBytes": 269443072, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 268992512, - "median": 270135296, - "p95": 309043200, - "max": 309043200 - } - }, - "baselineSelfA": { - "samples": [ - { - "peakRssBytes": 95563776, - "startupRssBytes": 65470464, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66633728, - "loadedPeakRssBytes": 68255744, - "resourceUsageMaxRssBytes": 95563776, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95862784, - "startupRssBytes": 65462272, - "loadedRssBytes": 67297280, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66928640, - "loadedPeakRssBytes": 68186112, - "resourceUsageMaxRssBytes": 95862784, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94965760, - "startupRssBytes": 65818624, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67014656, - "loadedPeakRssBytes": 68362240, - "resourceUsageMaxRssBytes": 94965760, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95129600, - "startupRssBytes": 65208320, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66326528, - "loadedPeakRssBytes": 68149248, - "resourceUsageMaxRssBytes": 94162944, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94371840, - "startupRssBytes": 64974848, - "loadedRssBytes": 66940928, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66097152, - "loadedPeakRssBytes": 67719168, - "resourceUsageMaxRssBytes": 94371840, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 94371840, - "median": 95129600, - "p95": 95862784, - "max": 95862784 - } - }, - "baselineSelfB": { - "samples": [ - { - "peakRssBytes": 96808960, - "startupRssBytes": 64933888, - "loadedRssBytes": 67424256, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66068480, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 96731136, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94896128, - "startupRssBytes": 65703936, - "loadedRssBytes": 67407872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 94896128, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84250624, - "startupRssBytes": 65826816, - "loadedRssBytes": 67530752, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66949120, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 97107968, - "startupRssBytes": 65032192, - "loadedRssBytes": 66998272, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66183168, - "loadedPeakRssBytes": 67727360, - "resourceUsageMaxRssBytes": 97107968, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94167040, - "startupRssBytes": 64937984, - "loadedRssBytes": 67428352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66555904, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 93442048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 84250624, - "median": 94896128, - "p95": 97107968, - "max": 97107968 - } - } - }, - "perLock": [ - { - "id": "ruined-tower-door", - "solvers": { - "candidate": { - "peakRssBytes": 77537280, - "startupRssBytes": 65085440, - "loadedRssBytes": 66658304, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66162688, - "loadedPeakRssBytes": 67620864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 74416128, - "startupRssBytes": 64368640, - "loadedRssBytes": 67383296, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65441792, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78462976, - "startupRssBytes": 65814528, - "loadedRssBytes": 67125248, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66789376, - "loadedPeakRssBytes": 67813376, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 106233856, - "startupRssBytes": 64303104, - "loadedRssBytes": 68497408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65495040, - "loadedPeakRssBytes": 69402624, - "resourceUsageMaxRssBytes": 105652224, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "scatty-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71573504, - "startupRssBytes": 65675264, - "loadedRssBytes": 67117056, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66748416, - "loadedPeakRssBytes": 67698688, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71282688, - "startupRssBytes": 65290240, - "loadedRssBytes": 67387392, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66768896, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78876672, - "startupRssBytes": 66211840, - "loadedRssBytes": 67260416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67403776, - "loadedPeakRssBytes": 68096000, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75640832, - "startupRssBytes": 65265664, - "loadedRssBytes": 68411392, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66207744, - "loadedPeakRssBytes": 69148672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "cor-galom-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72212480, - "startupRssBytes": 65150976, - "loadedRssBytes": 66592768, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66125824, - "loadedPeakRssBytes": 67354624, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71266304, - "startupRssBytes": 65617920, - "loadedRssBytes": 67452928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66957312, - "loadedPeakRssBytes": 68227072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 76562432, - "startupRssBytes": 65687552, - "loadedRssBytes": 67391488, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66940928, - "loadedPeakRssBytes": 68087808, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 73269248, - "startupRssBytes": 65531904, - "loadedRssBytes": 68415488, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67010560, - "loadedPeakRssBytes": 69345280, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "cor-galom-bedroom-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72265728, - "startupRssBytes": 65675264, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67072000, - "loadedPeakRssBytes": 68050944, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71311360, - "startupRssBytes": 65495040, - "loadedRssBytes": 67461120, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 68280320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78819328, - "startupRssBytes": 65101824, - "loadedRssBytes": 67198976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66678784, - "loadedPeakRssBytes": 68079616, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78999552, - "startupRssBytes": 65703936, - "loadedRssBytes": 68456448, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66990080, - "loadedPeakRssBytes": 69292032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "swamp-camp-forge-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72208384, - "startupRssBytes": 65228800, - "loadedRssBytes": 66670592, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66412544, - "loadedPeakRssBytes": 67661824, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71602176, - "startupRssBytes": 64950272, - "loadedRssBytes": 67571712, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65806336, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78815232, - "startupRssBytes": 65097728, - "loadedRssBytes": 67325952, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66359296, - "loadedPeakRssBytes": 68022272, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 83496960, - "startupRssBytes": 65830912, - "loadedRssBytes": 68714496, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67059712, - "loadedPeakRssBytes": 69357568, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "rice-lord-storeroom", - "solvers": { - "candidate": { - "peakRssBytes": 71999488, - "startupRssBytes": 65429504, - "loadedRssBytes": 66740224, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66564096, - "loadedPeakRssBytes": 67616768, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71303168, - "startupRssBytes": 65781760, - "loadedRssBytes": 67747840, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67104768, - "loadedPeakRssBytes": 68378624, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78467072, - "startupRssBytes": 65544192, - "loadedRssBytes": 66985984, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66797568, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75665408, - "startupRssBytes": 65040384, - "loadedRssBytes": 68448256, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65794048, - "loadedPeakRssBytes": 69210112, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-chambers-door", - "solvers": { - "candidate": { - "peakRssBytes": 72138752, - "startupRssBytes": 65363968, - "loadedRssBytes": 66936832, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66924544, - "loadedPeakRssBytes": 67854336, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71692288, - "startupRssBytes": 65499136, - "loadedRssBytes": 67203072, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66719744, - "loadedPeakRssBytes": 67993600, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79171584, - "startupRssBytes": 65282048, - "loadedRssBytes": 67248128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66682880, - "loadedPeakRssBytes": 68141056, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 80289792, - "startupRssBytes": 65380352, - "loadedRssBytes": 68395008, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67026944, - "loadedPeakRssBytes": 69320704, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-throne-hall-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71208960, - "startupRssBytes": 65286144, - "loadedRssBytes": 66727936, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66678784, - "loadedPeakRssBytes": 67715072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71991296, - "startupRssBytes": 65724416, - "loadedRssBytes": 67297280, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67112960, - "loadedPeakRssBytes": 68227072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 72019968, - "startupRssBytes": 65179648, - "loadedRssBytes": 67801088, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66400256, - "loadedPeakRssBytes": 68497408, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 72011776, - "startupRssBytes": 64962560, - "loadedRssBytes": 68632576, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66564096, - "loadedPeakRssBytes": 69357568, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "old-camp-dungeon-door", - "solvers": { - "candidate": { - "peakRssBytes": 77049856, - "startupRssBytes": 65257472, - "loadedRssBytes": 67092480, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66510848, - "loadedPeakRssBytes": 67887104, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 75661312, - "startupRssBytes": 65220608, - "loadedRssBytes": 67448832, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66314240, - "loadedPeakRssBytes": 68214784, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79208448, - "startupRssBytes": 63983616, - "loadedRssBytes": 66473984, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65093632, - "loadedPeakRssBytes": 67276800, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 85356544, - "startupRssBytes": 65171456, - "loadedRssBytes": 68448256, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66473984, - "loadedPeakRssBytes": 69226496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arlin-dungeon-key", - "solvers": { - "candidate": { - "peakRssBytes": 81268736, - "startupRssBytes": 65007616, - "loadedRssBytes": 66449408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66531328, - "loadedPeakRssBytes": 67469312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 79904768, - "startupRssBytes": 65724416, - "loadedRssBytes": 67428352, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66981888, - "loadedPeakRssBytes": 68276224, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 83525632, - "startupRssBytes": 64585728, - "loadedRssBytes": 67338240, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65556480, - "loadedPeakRssBytes": 68038656, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 76693504, - "startupRssBytes": 65495040, - "loadedRssBytes": 68509696, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66912256, - "loadedPeakRssBytes": 69320704, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-wine-cellar", - "solvers": { - "candidate": { - "peakRssBytes": 71606272, - "startupRssBytes": 65417216, - "loadedRssBytes": 66859008, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66609152, - "loadedPeakRssBytes": 67608576, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71225344, - "startupRssBytes": 65245184, - "loadedRssBytes": 67473408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66383872, - "loadedPeakRssBytes": 68194304, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79204352, - "startupRssBytes": 65544192, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66928640, - "loadedPeakRssBytes": 68059136, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75743232, - "startupRssBytes": 65306624, - "loadedRssBytes": 68583424, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 69271552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedside-chest", - "solvers": { - "candidate": { - "peakRssBytes": 76054528, - "startupRssBytes": 65183744, - "loadedRssBytes": 66887680, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66506752, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73519104, - "startupRssBytes": 64593920, - "loadedRssBytes": 67215360, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65310720, - "loadedPeakRssBytes": 67936256, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78987264, - "startupRssBytes": 64577536, - "loadedRssBytes": 67198976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65683456, - "loadedPeakRssBytes": 67956736, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78557184, - "startupRssBytes": 65847296, - "loadedRssBytes": 68599808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67043328, - "loadedPeakRssBytes": 69226496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-wall-chest", - "solvers": { - "candidate": { - "peakRssBytes": 79142912, - "startupRssBytes": 65122304, - "loadedRssBytes": 66957312, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66228224, - "loadedPeakRssBytes": 67846144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 76414976, - "startupRssBytes": 64720896, - "loadedRssBytes": 67080192, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65634304, - "loadedPeakRssBytes": 67944448, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79220736, - "startupRssBytes": 65282048, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66985984, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 111869952, - "startupRssBytes": 64249856, - "loadedRssBytes": 68444160, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65085440, - "loadedPeakRssBytes": 69144576, - "resourceUsageMaxRssBytes": 111263744, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "voran-bedside-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71966720, - "startupRssBytes": 65683456, - "loadedRssBytes": 66994176, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67055616, - "loadedPeakRssBytes": 67956736, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71983104, - "startupRssBytes": 65404928, - "loadedRssBytes": 67502080, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66428928, - "loadedPeakRssBytes": 68198400, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 83914752, - "startupRssBytes": 65208320, - "loadedRssBytes": 67436544, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66514944, - "loadedPeakRssBytes": 68112384, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 123584512, - "startupRssBytes": 65044480, - "loadedRssBytes": 68190208, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66375680, - "loadedPeakRssBytes": 69156864, - "resourceUsageMaxRssBytes": 122744832, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arto-room-door", - "solvers": { - "candidate": { - "peakRssBytes": 71913472, - "startupRssBytes": 65241088, - "loadedRssBytes": 66551808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66400256, - "loadedPeakRssBytes": 67444736, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70885376, - "startupRssBytes": 65437696, - "loadedRssBytes": 67272704, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66719744, - "loadedPeakRssBytes": 67952640, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78823424, - "startupRssBytes": 65675264, - "loadedRssBytes": 67248128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67026944, - "loadedPeakRssBytes": 68091904, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74354688, - "startupRssBytes": 65679360, - "loadedRssBytes": 68038656, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66564096, - "loadedPeakRssBytes": 68825088, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arto-far-bed-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72302592, - "startupRssBytes": 65015808, - "loadedRssBytes": 66326528, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66314240, - "loadedPeakRssBytes": 67268608, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71376896, - "startupRssBytes": 65216512, - "loadedRssBytes": 67575808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66478080, - "loadedPeakRssBytes": 68255744, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78929920, - "startupRssBytes": 65646592, - "loadedRssBytes": 67219456, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66936832, - "loadedPeakRssBytes": 68046848, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74788864, - "startupRssBytes": 65531904, - "loadedRssBytes": 68546560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66867200, - "loadedPeakRssBytes": 69308416, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arto-near-bed-chest", - "solvers": { - "candidate": { - "peakRssBytes": 73064448, - "startupRssBytes": 64270336, - "loadedRssBytes": 65974272, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65445888, - "loadedPeakRssBytes": 66961408, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71794688, - "startupRssBytes": 64516096, - "loadedRssBytes": 66088960, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65302528, - "loadedPeakRssBytes": 66691072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78839808, - "startupRssBytes": 65679360, - "loadedRssBytes": 67383296, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66977792, - "loadedPeakRssBytes": 68071424, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 110370816, - "startupRssBytes": 64880640, - "loadedRssBytes": 68419584, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66088960, - "loadedPeakRssBytes": 69353472, - "resourceUsageMaxRssBytes": 109670400, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedrooms-door", - "solvers": { - "candidate": { - "peakRssBytes": 79302656, - "startupRssBytes": 65355776, - "loadedRssBytes": 66928640, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66699264, - "loadedPeakRssBytes": 67833856, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 78962688, - "startupRssBytes": 65097728, - "loadedRssBytes": 67325952, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66465792, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79081472, - "startupRssBytes": 64839680, - "loadedRssBytes": 67067904, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65900544, - "loadedPeakRssBytes": 67969024, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 84283392, - "startupRssBytes": 64897024, - "loadedRssBytes": 68698112, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65888256, - "loadedPeakRssBytes": 69361664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedrooms-far-chest", - "solvers": { - "candidate": { - "peakRssBytes": 75325440, - "startupRssBytes": 65163264, - "loadedRssBytes": 66867200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66473984, - "loadedPeakRssBytes": 67735552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73637888, - "startupRssBytes": 65191936, - "loadedRssBytes": 67420160, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66863104, - "loadedPeakRssBytes": 68190208, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79294464, - "startupRssBytes": 67203072, - "loadedRssBytes": 67596288, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67887104, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75583488, - "startupRssBytes": 65593344, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66969600, - "loadedPeakRssBytes": 69156864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedrooms-near-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71790592, - "startupRssBytes": 64991232, - "loadedRssBytes": 66301952, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65740800, - "loadedPeakRssBytes": 67002368, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71270400, - "startupRssBytes": 64344064, - "loadedRssBytes": 67489792, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65359872, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79159296, - "startupRssBytes": 64802816, - "loadedRssBytes": 67293184, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66056192, - "loadedPeakRssBytes": 68067328, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 85274624, - "startupRssBytes": 65421312, - "loadedRssBytes": 68435968, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 69005312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-left-chest", - "solvers": { - "candidate": { - "peakRssBytes": 75079680, - "startupRssBytes": 65523712, - "loadedRssBytes": 66965504, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66568192, - "loadedPeakRssBytes": 67747840, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73498624, - "startupRssBytes": 65802240, - "loadedRssBytes": 67244032, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66772992, - "loadedPeakRssBytes": 67981312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79056896, - "startupRssBytes": 64958464, - "loadedRssBytes": 67186688, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65679360, - "loadedPeakRssBytes": 68022272, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 92065792, - "startupRssBytes": 64827392, - "loadedRssBytes": 68497408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66138112, - "loadedPeakRssBytes": 69206016, - "resourceUsageMaxRssBytes": 91205632, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-right-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72089600, - "startupRssBytes": 65638400, - "loadedRssBytes": 67211264, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67026944, - "loadedPeakRssBytes": 67960832, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72290304, - "startupRssBytes": 66674688, - "loadedRssBytes": 67592192, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67465216, - "loadedPeakRssBytes": 68386816, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79151104, - "startupRssBytes": 64765952, - "loadedRssBytes": 67256320, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65757184, - "loadedPeakRssBytes": 68038656, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 76038144, - "startupRssBytes": 65339392, - "loadedRssBytes": 68354048, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66514944, - "loadedPeakRssBytes": 69271552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor2-stairs-chest", - "solvers": { - "candidate": { - "peakRssBytes": 78045184, - "startupRssBytes": 64905216, - "loadedRssBytes": 66740224, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66195456, - "loadedPeakRssBytes": 67358720, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 75186176, - "startupRssBytes": 64901120, - "loadedRssBytes": 67260416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66289664, - "loadedPeakRssBytes": 68206592, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78721024, - "startupRssBytes": 64835584, - "loadedRssBytes": 67063808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66416640, - "loadedPeakRssBytes": 67735552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 93245440, - "startupRssBytes": 65855488, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66990080, - "loadedPeakRssBytes": 69308416, - "resourceUsageMaxRssBytes": 92598272, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor2-chest-2", - "solvers": { - "candidate": { - "peakRssBytes": 72044544, - "startupRssBytes": 65884160, - "loadedRssBytes": 67063808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67031040, - "loadedPeakRssBytes": 67891200, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72298496, - "startupRssBytes": 65290240, - "loadedRssBytes": 67256320, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66572288, - "loadedPeakRssBytes": 68222976, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78888960, - "startupRssBytes": 65081344, - "loadedRssBytes": 67178496, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66191360, - "loadedPeakRssBytes": 68038656, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74493952, - "startupRssBytes": 64372736, - "loadedRssBytes": 68567040, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65331200, - "loadedPeakRssBytes": 69373952, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor3-entrance-chest", - "solvers": { - "candidate": { - "peakRssBytes": 73695232, - "startupRssBytes": 65236992, - "loadedRssBytes": 66547712, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66519040, - "loadedPeakRssBytes": 67457024, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73441280, - "startupRssBytes": 65343488, - "loadedRssBytes": 67702784, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66543616, - "loadedPeakRssBytes": 68182016, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78643200, - "startupRssBytes": 65867776, - "loadedRssBytes": 67047424, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66863104, - "loadedPeakRssBytes": 67829760, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 76640256, - "startupRssBytes": 65904640, - "loadedRssBytes": 68657152, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66973696, - "loadedPeakRssBytes": 69349376, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor3-empty-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71852032, - "startupRssBytes": 66080768, - "loadedRssBytes": 66998272, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67137536, - "loadedPeakRssBytes": 67940352, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71245824, - "startupRssBytes": 65163264, - "loadedRssBytes": 67260416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66428928, - "loadedPeakRssBytes": 68218880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78536704, - "startupRssBytes": 65486848, - "loadedRssBytes": 66928640, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66781184, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75935744, - "startupRssBytes": 64512000, - "loadedRssBytes": 68575232, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65622016, - "loadedPeakRssBytes": 69304320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor4-elixirs-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72155136, - "startupRssBytes": 64491520, - "loadedRssBytes": 66719744, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65703936, - "loadedPeakRssBytes": 67670016, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71299072, - "startupRssBytes": 65552384, - "loadedRssBytes": 67387392, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 82427904, - "startupRssBytes": 65687552, - "loadedRssBytes": 66736128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66732032, - "loadedPeakRssBytes": 67620864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78761984, - "startupRssBytes": 65454080, - "loadedRssBytes": 68730880, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 69373952, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor4-food-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72327168, - "startupRssBytes": 64372736, - "loadedRssBytes": 65421312, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65400832, - "loadedPeakRssBytes": 66535424, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71421952, - "startupRssBytes": 65310720, - "loadedRssBytes": 67407872, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66527232, - "loadedPeakRssBytes": 68280320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79224832, - "startupRssBytes": 65339392, - "loadedRssBytes": 67174400, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66560000, - "loadedPeakRssBytes": 68120576, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 93716480, - "startupRssBytes": 65572864, - "loadedRssBytes": 67801088, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66535424, - "loadedPeakRssBytes": 68571136, - "resourceUsageMaxRssBytes": 93716480, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-top-chest", - "solvers": { - "candidate": { - "peakRssBytes": 79269888, - "startupRssBytes": 66072576, - "loadedRssBytes": 67121152, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67076096, - "loadedPeakRssBytes": 67948544, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 76902400, - "startupRssBytes": 65351680, - "loadedRssBytes": 67710976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66949120, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78548992, - "startupRssBytes": 65146880, - "loadedRssBytes": 66981888, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 67817472, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 86102016, - "startupRssBytes": 65794048, - "loadedRssBytes": 68546560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66965504, - "loadedPeakRssBytes": 69246976, - "resourceUsageMaxRssBytes": 85250048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "fire-lizard-cave-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71999488, - "startupRssBytes": 65421312, - "loadedRssBytes": 67125248, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66793472, - "loadedPeakRssBytes": 67944448, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71417856, - "startupRssBytes": 64843776, - "loadedRssBytes": 67334144, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66064384, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79163392, - "startupRssBytes": 65540096, - "loadedRssBytes": 66850816, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66707456, - "loadedPeakRssBytes": 67694592, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 94773248, - "startupRssBytes": 65036288, - "loadedRssBytes": 68706304, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66277376, - "loadedPeakRssBytes": 69341184, - "resourceUsageMaxRssBytes": 94773248, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "silas-tavern-basement-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72122368, - "startupRssBytes": 64565248, - "loadedRssBytes": 66269184, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65400832, - "loadedPeakRssBytes": 66957312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71405568, - "startupRssBytes": 65630208, - "loadedRssBytes": 67727360, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66318336, - "loadedPeakRssBytes": 68210688, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78974976, - "startupRssBytes": 65765376, - "loadedRssBytes": 67600384, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67031040, - "loadedPeakRssBytes": 68091904, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74752000, - "startupRssBytes": 65855488, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67010560, - "loadedPeakRssBytes": 69316608, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "silas-tavern-storeroom-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71946240, - "startupRssBytes": 66117632, - "loadedRssBytes": 67166208, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67235840, - "loadedPeakRssBytes": 67985408, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71139328, - "startupRssBytes": 65794048, - "loadedRssBytes": 67760128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67022848, - "loadedPeakRssBytes": 68370432, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79196160, - "startupRssBytes": 65155072, - "loadedRssBytes": 67252224, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66383872, - "loadedPeakRssBytes": 68141056, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78635008, - "startupRssBytes": 64790528, - "loadedRssBytes": 68329472, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66011136, - "loadedPeakRssBytes": 69222400, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "silas-tavern-storeroom-door", - "solvers": { - "candidate": { - "peakRssBytes": 72241152, - "startupRssBytes": 64372736, - "loadedRssBytes": 65290240, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65490944, - "loadedPeakRssBytes": 66146304, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71512064, - "startupRssBytes": 64557056, - "loadedRssBytes": 67440640, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65273856, - "loadedPeakRssBytes": 68132864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78893056, - "startupRssBytes": 65040384, - "loadedRssBytes": 67399680, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66592768, - "loadedPeakRssBytes": 68124672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75194368, - "startupRssBytes": 65142784, - "loadedRssBytes": 68419584, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66314240, - "loadedPeakRssBytes": 69242880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "jan-pond-house-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71491584, - "startupRssBytes": 65925120, - "loadedRssBytes": 66842624, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66760704, - "loadedPeakRssBytes": 67538944, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70500352, - "startupRssBytes": 65695744, - "loadedRssBytes": 67137536, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66633728, - "loadedPeakRssBytes": 67809280, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78970880, - "startupRssBytes": 65470464, - "loadedRssBytes": 67436544, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66777088, - "loadedPeakRssBytes": 68116480, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 93093888, - "startupRssBytes": 65413120, - "loadedRssBytes": 68034560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66576384, - "loadedPeakRssBytes": 68816896, - "resourceUsageMaxRssBytes": 91906048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "graham-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72069120, - "startupRssBytes": 64765952, - "loadedRssBytes": 66338816, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65581056, - "loadedPeakRssBytes": 67194880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71299072, - "startupRssBytes": 65204224, - "loadedRssBytes": 67563520, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66428928, - "loadedPeakRssBytes": 68218880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78872576, - "startupRssBytes": 65208320, - "loadedRssBytes": 67305472, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66457600, - "loadedPeakRssBytes": 68055040, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 79687680, - "startupRssBytes": 65462272, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66789376, - "loadedPeakRssBytes": 69304320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gravo-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72118272, - "startupRssBytes": 64983040, - "loadedRssBytes": 66686976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66228224, - "loadedPeakRssBytes": 67579904, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70488064, - "startupRssBytes": 65179648, - "loadedRssBytes": 66883584, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66408448, - "loadedPeakRssBytes": 67633152, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79040512, - "startupRssBytes": 65675264, - "loadedRssBytes": 67117056, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66781184, - "loadedPeakRssBytes": 67846144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75771904, - "startupRssBytes": 65347584, - "loadedRssBytes": 68624384, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66322432, - "loadedPeakRssBytes": 69361664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "diego-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72146944, - "startupRssBytes": 65880064, - "loadedRssBytes": 67190784, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67063808, - "loadedPeakRssBytes": 68005888, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72073216, - "startupRssBytes": 65470464, - "loadedRssBytes": 67567616, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66408448, - "loadedPeakRssBytes": 68186112, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78852096, - "startupRssBytes": 65925120, - "loadedRssBytes": 67366912, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67059712, - "loadedPeakRssBytes": 68120576, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78807040, - "startupRssBytes": 65200128, - "loadedRssBytes": 68345856, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66367488, - "loadedPeakRssBytes": 69296128, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "grim-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72241152, - "startupRssBytes": 65159168, - "loadedRssBytes": 66994176, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66891776, - "loadedPeakRssBytes": 67862528, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71405568, - "startupRssBytes": 65433600, - "loadedRssBytes": 67399680, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66494464, - "loadedPeakRssBytes": 68214784, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78860288, - "startupRssBytes": 65060864, - "loadedRssBytes": 67158016, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66310144, - "loadedPeakRssBytes": 68034560, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75022336, - "startupRssBytes": 65683456, - "loadedRssBytes": 68567040, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 69443584, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "mercenary-near-grim-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71467008, - "startupRssBytes": 65024000, - "loadedRssBytes": 66072576, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66228224, - "loadedPeakRssBytes": 66879488, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71958528, - "startupRssBytes": 65830912, - "loadedRssBytes": 67403776, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66977792, - "loadedPeakRssBytes": 68313088, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78442496, - "startupRssBytes": 65724416, - "loadedRssBytes": 66904064, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66744320, - "loadedPeakRssBytes": 67756032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78852096, - "startupRssBytes": 64790528, - "loadedRssBytes": 68591616, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65409024, - "loadedPeakRssBytes": 69369856, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "popular-lock", - "solvers": { - "candidate": { - "peakRssBytes": 72925184, - "startupRssBytes": 64864256, - "loadedRssBytes": 67223552, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66072576, - "loadedPeakRssBytes": 67854336, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72818688, - "startupRssBytes": 65572864, - "loadedRssBytes": 67670016, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66842624, - "loadedPeakRssBytes": 68358144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78925824, - "startupRssBytes": 64843776, - "loadedRssBytes": 67465216, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66355200, - "loadedPeakRssBytes": 68124672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75907072, - "startupRssBytes": 65937408, - "loadedRssBytes": 67903488, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66772992, - "loadedPeakRssBytes": 68808704, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "alberto-mine-hut-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72032256, - "startupRssBytes": 65372160, - "loadedRssBytes": 66945024, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66519040, - "loadedPeakRssBytes": 67907584, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71299072, - "startupRssBytes": 66199552, - "loadedRssBytes": 67772416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67170304, - "loadedPeakRssBytes": 68464640, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78888960, - "startupRssBytes": 65069056, - "loadedRssBytes": 67166208, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66940928, - "loadedPeakRssBytes": 68124672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75685888, - "startupRssBytes": 65064960, - "loadedRssBytes": 68472832, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66490368, - "loadedPeakRssBytes": 69316608, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "alberto-mine-upstairs-left-chest", - "solvers": { - "candidate": { - "peakRssBytes": 70963200, - "startupRssBytes": 65175552, - "loadedRssBytes": 67010560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66551808, - "loadedPeakRssBytes": 67665920, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71032832, - "startupRssBytes": 65646592, - "loadedRssBytes": 67481600, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68358144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 71507968, - "startupRssBytes": 65769472, - "loadedRssBytes": 67080192, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66801664, - "loadedPeakRssBytes": 67776512, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 72077312, - "startupRssBytes": 65044480, - "loadedRssBytes": 68321280, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66244608, - "loadedPeakRssBytes": 69312512, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "alberto-mine-upstairs-right-chest", - "solvers": { - "candidate": { - "peakRssBytes": 70955008, - "startupRssBytes": 65347584, - "loadedRssBytes": 66658304, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 67461120, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70983680, - "startupRssBytes": 65761280, - "loadedRssBytes": 67334144, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66994176, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78843904, - "startupRssBytes": 65380352, - "loadedRssBytes": 67477504, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66445312, - "loadedPeakRssBytes": 68112384, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 73433088, - "startupRssBytes": 65761280, - "loadedRssBytes": 67727360, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66560000, - "loadedPeakRssBytes": 68489216, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "cavalorn-storeroom-door", - "solvers": { - "candidate": { - "peakRssBytes": 72126464, - "startupRssBytes": 65568768, - "loadedRssBytes": 67010560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66990080, - "loadedPeakRssBytes": 67907584, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71397376, - "startupRssBytes": 65282048, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66547712, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79187968, - "startupRssBytes": 65269760, - "loadedRssBytes": 67366912, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66609152, - "loadedPeakRssBytes": 68096000, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 85778432, - "startupRssBytes": 65150976, - "loadedRssBytes": 68558848, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66166784, - "loadedPeakRssBytes": 69103616, - "resourceUsageMaxRssBytes": 85106688, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "old-to-new-camp-bandits-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72785920, - "startupRssBytes": 65982464, - "loadedRssBytes": 67162112, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67121152, - "loadedPeakRssBytes": 67997696, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71737344, - "startupRssBytes": 65581056, - "loadedRssBytes": 67416064, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66609152, - "loadedPeakRssBytes": 68075520, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79044608, - "startupRssBytes": 65212416, - "loadedRssBytes": 67309568, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66351104, - "loadedPeakRssBytes": 68112384, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74752000, - "startupRssBytes": 65073152, - "loadedRssBytes": 68218880, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66187264, - "loadedPeakRssBytes": 69152768, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - } - ] - }, - "config": { - "repetitions": 7, - "warmups": 2, - "lockCount": 45, - "scope": "full-45-catalog", - "timingBoundary": "Only synchronous public solver call; cloning, legacy/reference normalization, replay and metrics excluded.", - "order": "Per-lock baseline-vs-itself calibration first; matched rounds rotate by lock and round. A non-pass receives one bounded repeat with all raw attempts retained.", - "uncertainty": "Paired log-ratio mean with conservative 2.776 standard-error envelope; control sets tolerance. >=5 samples; >25% control envelope cannot certify a pass.", - "gc": "Natural GC; no forced collection.", - "fixtureSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675" - }, - "aggregates": { - "originalBfs": { - "A": 483, - "U": 245, - "C": 1727, - "plateSwitches": 438, - "sumOfMediansMs": 2653.7006490000126 - }, - "upstream": { - "A": 491, - "U": 245, - "C": 1695, - "plateSwitches": 446, - "sumOfMediansMs": 1517.4034640000011 - }, - "candidate": { - "A": 483, - "U": 245, - "C": 1723, - "plateSwitches": 438, - "sumOfMediansMs": 47.3627580000109 - }, - "baseline": { - "A": 483, - "U": 245, - "C": 1723, - "plateSwitches": 438, - "sumOfMediansMs": 48.507443999980296 - } - }, - "reports": { - "json": { - "path": "benchmark.json", - "sha256": "066f75d89ae450a2e0697d0e94b37864adbc81c6ff0e902eda3bea7a38e9ed81" - }, - "markdown": { - "path": "benchmark.md", - "sha256": "2b3926535fc7c0a385a4e3d5e5d89b5367e93359dd0a76b8dcf8539315e2e2ba" - } - } -} diff --git a/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.json b/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.json deleted file mode 100644 index 08ad7f9..0000000 --- a/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.json +++ /dev/null @@ -1,25093 +0,0 @@ -{ - "schemaVersion": 1, - "createdAt": "2026-09-07T09:12:21.367Z", - "config": { - "repetitions": 7, - "warmups": 2, - "lockCount": 45, - "scope": "full-45-catalog", - "timingBoundary": "Only synchronous public solver call; cloning, legacy/reference normalization, replay and metrics excluded.", - "order": "Per-lock baseline-vs-itself calibration first; matched rounds rotate by lock and round. A non-pass receives one bounded repeat with all raw attempts retained.", - "uncertainty": "Paired log-ratio mean with conservative 2.776 standard-error envelope; control sets tolerance. >=5 samples; >25% control envelope cannot certify a pass.", - "gc": "Natural GC; no forced collection.", - "fixtureSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675" - }, - "correctness": { - "passed": true - }, - "quality": "passed", - "performance": "inconclusive", - "implementations": { - "originalBfs": "Original Apache BFS", - "upstream": "UnlockMyLoot pinned external reference", - "candidate": "Candidate TypeScript library", - "baseline": "Target/reference baseline" - }, - "aggregates": { - "originalBfs": { - "A": 483, - "U": 245, - "C": 1727, - "plateSwitches": 438, - "sumOfMediansMs": 2653.7006490000126 - }, - "upstream": { - "A": 491, - "U": 245, - "C": 1695, - "plateSwitches": 446, - "sumOfMediansMs": 1517.4034640000011 - }, - "candidate": { - "A": 483, - "U": 245, - "C": 1723, - "plateSwitches": 438, - "sumOfMediansMs": 47.3627580000109 - }, - "baseline": { - "A": 483, - "U": 245, - "C": 1723, - "plateSwitches": 438, - "sumOfMediansMs": 48.507443999980296 - } - }, - "results": [ - { - "id": "ruined-tower-door", - "title": "Ruined Tower door", - "optimumA": 17, - "solvers": { - "originalBfs": { - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - [ - 0, - -4 - ], - [ - 2, - 5 - ], - [ - 3, - -6 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 5, - -2 - ], - [ - 0, - -4 - ], - [ - 1, - 3 - ], - [ - 3, - -5 - ], - [ - 4, - -3 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 1, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 93.975911, - 79.51385199999993, - 252.6963740000001, - 97.26116200000001, - 89.65834799999993, - 85.219783, - 86.95025499999997 - ], - "timingMs": { - "min": 79.51385199999993, - "median": 89.65834799999993, - "p95": 252.6963740000001, - "max": 252.6963740000001 - } - }, - "upstream": { - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - [ - 2, - 5 - ], - [ - 3, - -5 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 0, - -5 - ], - [ - 5, - -2 - ], - [ - 1, - 3 - ], - [ - 4, - -3 - ], - [ - 0, - -6 - ], - [ - 5, - -3 - ], - [ - 3, - -6 - ], - [ - 1, - 6 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 132.731356, - 82.08698200000003, - 99.24884099999986, - 93.8309240000001, - 94.43956899999989, - 91.279086, - 80.26694399999997 - ], - "timingMs": { - "min": 80.26694399999997, - "median": 93.8309240000001, - "p95": 132.731356, - "max": 132.731356 - } - }, - "candidate": { - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - [ - 0, - -4 - ], - [ - 2, - 5 - ], - [ - 3, - -6 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 5, - -2 - ], - [ - 0, - -4 - ], - [ - 1, - 3 - ], - [ - 3, - -5 - ], - [ - 4, - -3 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 1, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 4.910295000000019, - 3.461626000000024, - 4.107326999999941, - 3.5131129999999757, - 3.577199000000064, - 3.8338260000000446, - 3.4595429999999396 - ], - "timingMs": { - "min": 3.4595429999999396, - "median": 3.577199000000064, - "p95": 4.910295000000019, - "max": 4.910295000000019 - } - }, - "baseline": { - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - [ - 0, - -4 - ], - [ - 2, - 5 - ], - [ - 3, - -6 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 5, - -2 - ], - [ - 0, - -4 - ], - [ - 1, - 3 - ], - [ - 3, - -5 - ], - [ - 4, - -3 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 1, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 3.508687000000009, - 5.843217999999979, - 3.7145660000001044, - 3.319001999999955, - 3.333893999999873, - 5.0080820000000585, - 3.165269999999964 - ], - "timingMs": { - "min": 3.165269999999964, - "median": 3.508687000000009, - "p95": 5.843217999999979, - "max": 5.843217999999979 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.9804851261710085, - "noiseRatio": 1.357658308620493, - "lowerRatio": 0.7282804758163847, - "upperRatio": 1.3200286353481152 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9804851261710085, - "noiseRatio": 1.357658308620493, - "lowerRatio": 0.7282804758163847, - "upperRatio": 1.3200286353481152, - "candidate": [ - 4.910295000000019, - 3.461626000000024, - 4.107326999999941, - 3.5131129999999757, - 3.577199000000064, - 3.8338260000000446, - 3.4595429999999396 - ], - "baseline": [ - 3.508687000000009, - 5.843217999999979, - 3.7145660000001044, - 3.319001999999955, - 3.333893999999873, - 5.0080820000000585, - 3.165269999999964 - ], - "selfA": [ - 3.5670550000000105, - 6.0774000000000115, - 3.186813000000001, - 3.5614960000000053, - 4.496512999999993, - 5.546760999999975, - 4.151252999999997 - ], - "selfB": [ - 5.820503000000002, - 5.289063999999996, - 3.3500589999999875, - 3.5607949999999846, - 3.0661910000000034, - 5.378298999999998, - 3.537520999999998 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.8903081529797303, - "noiseRatio": 1.419399645461027, - "lowerRatio": 0.5937780456252367, - "upperRatio": 1.334924073232676, - "candidate": [ - 3.3646300000000338, - 3.1216850000000704, - 3.7707299999997304, - 2.944879000000128, - 6.317911999999978, - 3.1307779999997365, - 3.08801399999993 - ], - "baseline": [ - 3.139651999999842, - 3.1827769999999873, - 3.1163060000003497, - 7.7678820000001, - 6.822991000000002, - 3.0908190000000104, - 3.14965600000005 - ], - "selfA": [ - 3.6906999999996515, - 3.0568869999997332, - 3.35890100000006, - 4.635651999999936, - 3.040171999999984, - 5.106238999999732, - 3.375757000000249 - ], - "selfB": [ - 3.215625999999702, - 3.1390299999998206, - 6.096318000000338, - 3.0291560000000572, - 3.0314589999998134, - 4.34230000000025, - 3.183157000000392 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 3.5670550000000105, - 6.0774000000000115, - 3.186813000000001, - 3.5614960000000053, - 4.496512999999993, - 5.546760999999975, - 4.151252999999997 - ], - "baselineSelfB": [ - 5.820503000000002, - 5.289063999999996, - 3.3500589999999875, - 3.5607949999999846, - 3.0661910000000034, - 5.378298999999998, - 3.537520999999998 - ] - }, - "historicalReference": { - "A": 17, - "U": 6, - "C": 57, - "plateSwitches": 16, - "commands": [ - [ - 2, - 5 - ], - [ - 3, - -5 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 1, - 1 - ], - [ - 4, - -1 - ], - [ - 0, - -5 - ], - [ - 5, - -2 - ], - [ - 1, - 3 - ], - [ - 4, - -3 - ], - [ - 0, - -6 - ], - [ - 5, - -3 - ], - [ - 3, - -6 - ], - [ - 1, - 6 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "scatty-chest", - "title": "Scatty's chest", - "optimumA": 9, - "solvers": { - "originalBfs": { - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 2, - 4 - ], - [ - 4, - 6 - ], - [ - 3, - 5 - ], - [ - 0, - -2 - ], - [ - 4, - 5 - ], - [ - 1, - -2 - ], - [ - 2, - 3 - ], - [ - 4, - 2 - ] - ], - "samplesMs": [ - 33.7264110000001, - 12.547288999999637, - 14.25079199999982, - 11.793896000000132, - 11.134845000000041, - 12.145755999999892, - 12.405906999999843 - ], - "timingMs": { - "min": 11.134845000000041, - "median": 12.405906999999843, - "p95": 33.7264110000001, - "max": 33.7264110000001 - } - }, - "upstream": { - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - 5 - ], - [ - 1, - -1 - ], - [ - 4, - 6 - ], - [ - 3, - 5 - ], - [ - 4, - 5 - ], - [ - 2, - 2 - ], - [ - 4, - 2 - ], - [ - 1, - -3 - ], - [ - 0, - -2 - ] - ], - "samplesMs": [ - 6.514267000000018, - 5.445058000000245, - 5.767013000000134, - 5.647273000000041, - 5.285999000000174, - 5.755354999999781, - 5.33290100000022 - ], - "timingMs": { - "min": 5.285999000000174, - "median": 5.647273000000041, - "p95": 6.514267000000018, - "max": 6.514267000000018 - } - }, - "candidate": { - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - 5 - ], - [ - 4, - 5 - ], - [ - 1, - -1 - ], - [ - 3, - 5 - ], - [ - 4, - 2 - ], - [ - 0, - -2 - ], - [ - 2, - 2 - ], - [ - 4, - 6 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 0.14935400000013033, - 0.13378099999999904, - 0.1606019999999262, - 0.13623499999994237, - 0.1360740000000078, - 0.1368959999999788, - 0.16545900000028269 - ], - "timingMs": { - "min": 0.13378099999999904, - "median": 0.1368959999999788, - "p95": 0.16545900000028269, - "max": 0.16545900000028269 - } - }, - "baseline": { - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - 5 - ], - [ - 4, - 5 - ], - [ - 1, - -1 - ], - [ - 3, - 5 - ], - [ - 4, - 2 - ], - [ - 0, - -2 - ], - [ - 2, - 2 - ], - [ - 4, - 6 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 0.1980679999996937, - 0.10514899999998306, - 0.1159349999998085, - 0.1378669999999147, - 0.11276999999972759, - 0.18676099999993312, - 0.14965499999971144 - ], - "timingMs": { - "min": 0.10514899999998306, - "median": 0.1378669999999147, - "p95": 0.1980679999996937, - "max": 0.1980679999996937 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.0363829747101125, - "noiseRatio": 2.0246954191039723, - "lowerRatio": 0.7967228074032943, - "upperRatio": 1.348134709196654 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0363829747101125, - "noiseRatio": 2.0246954191039723, - "lowerRatio": 0.7967228074032943, - "upperRatio": 1.348134709196654, - "candidate": [ - 0.14935400000013033, - 0.13378099999999904, - 0.1606019999999262, - 0.13623499999994237, - 0.1360740000000078, - 0.1368959999999788, - 0.16545900000028269 - ], - "baseline": [ - 0.1980679999996937, - 0.10514899999998306, - 0.1159349999998085, - 0.1378669999999147, - 0.11276999999972759, - 0.18676099999993312, - 0.14965499999971144 - ], - "selfA": [ - 0.11393099999986589, - 0.06770199999982651, - 0.06397599999991144, - 0.062163000000055035, - 0.07027599999992162, - 0.06251400000019203, - 0.07274899999993067 - ], - "selfB": [ - 0.0724289999998291, - 0.06700099999989106, - 0.0647369999996954, - 0.06660000000010768, - 0.062022999999953754, - 0.06314499999962209, - 0.273330999999871 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.4873576036137215, - "noiseRatio": 1.4780907145708593, - "lowerRatio": 1.131960417757932, - "upperRatio": 1.9543374541393506, - "candidate": [ - 0.1886939999999413, - 0.16295599999966726, - 0.13485300000002098, - 0.20311499999979787, - 0.08927399999993213, - 0.09652599999981248, - 0.08987500000012005 - ], - "baseline": [ - 0.1297150000000329, - 0.12473699999964083, - 0.07942899999989095, - 0.08088200000020151, - 0.07300900000018373, - 0.07023499999968408, - 0.07599500000014814 - ], - "selfA": [ - 0.12841299999990952, - 0.21343100000012782, - 0.08633999999983644, - 0.07011599999987084, - 0.07784700000001976, - 0.06522799999993367, - 0.06732099999999264 - ], - "selfB": [ - 0.12769200000002456, - 0.11864900000000489, - 0.07062600000017483, - 0.0658490000000711, - 0.07428200000003926, - 0.11194900000009511, - 0.0753629999999248 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.11393099999986589, - 0.06770199999982651, - 0.06397599999991144, - 0.062163000000055035, - 0.07027599999992162, - 0.06251400000019203, - 0.07274899999993067 - ], - "baselineSelfB": [ - 0.0724289999998291, - 0.06700099999989106, - 0.0647369999996954, - 0.06660000000010768, - 0.062022999999953754, - 0.06314499999962209, - 0.273330999999871 - ] - }, - "historicalReference": { - "A": 9, - "U": 5, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - 5 - ], - [ - 1, - -1 - ], - [ - 4, - 6 - ], - [ - 3, - 5 - ], - [ - 4, - 5 - ], - [ - 2, - 2 - ], - [ - 4, - 2 - ], - [ - 1, - -3 - ], - [ - 0, - -2 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "cor-galom-chest", - "title": "Cor Galom's chest", - "optimumA": 5, - "solvers": { - "originalBfs": { - "A": 5, - "U": 4, - "C": 19, - "plateSwitches": 4, - "commands": [ - [ - 3, - 6 - ], - [ - 1, - -5 - ], - [ - 0, - 4 - ], - [ - 2, - -2 - ], - [ - 3, - -2 - ] - ], - "samplesMs": [ - 0.5159859999998844, - 0.5061309999996411, - 0.4506079999996473, - 0.5328309999999874, - 0.45438300000023446, - 0.49082799999996496, - 0.5519100000001345 - ], - "timingMs": { - "min": 0.4506079999996473, - "median": 0.5061309999996411, - "p95": 0.5519100000001345, - "max": 0.5519100000001345 - } - }, - "upstream": { - "A": 5, - "U": 4, - "C": 15, - "plateSwitches": 4, - "commands": [ - [ - 3, - 4 - ], - [ - 1, - -4 - ], - [ - 2, - -2 - ], - [ - 0, - 4 - ], - [ - 1, - -1 - ] - ], - "samplesMs": [ - 0.33120800000006057, - 0.3216040000002067, - 0.427672999999686, - 0.3339730000002419, - 0.34301500000037777, - 0.4625960000003033, - 0.37169900000026246 - ], - "timingMs": { - "min": 0.3216040000002067, - "median": 0.34301500000037777, - "p95": 0.4625960000003033, - "max": 0.4625960000003033 - } - }, - "candidate": { - "A": 5, - "U": 4, - "C": 15, - "plateSwitches": 4, - "commands": [ - [ - 3, - 4 - ], - [ - 1, - -4 - ], - [ - 0, - 4 - ], - [ - 1, - -1 - ], - [ - 2, - -2 - ] - ], - "samplesMs": [ - 0.09535400000004302, - 0.06094100000018443, - 0.04940499999975145, - 0.054341999999905966, - 0.1158940000000257, - 0.08681000000024142, - 0.08066099999996368 - ], - "timingMs": { - "min": 0.04940499999975145, - "median": 0.08066099999996368, - "p95": 0.1158940000000257, - "max": 0.1158940000000257 - } - }, - "baseline": { - "A": 5, - "U": 4, - "C": 15, - "plateSwitches": 4, - "commands": [ - [ - 3, - 4 - ], - [ - 1, - -4 - ], - [ - 0, - 4 - ], - [ - 1, - -1 - ], - [ - 2, - -2 - ] - ], - "samplesMs": [ - 0.04224400000020978, - 0.03475200000002587, - 0.03346000000010463, - 0.04046100000005026, - 0.057396000000153435, - 0.03976999999986219, - 0.133661000000302 - ], - "timingMs": { - "min": 0.03346000000010463, - "median": 0.04046100000005026, - "p95": 0.133661000000302, - "max": 0.133661000000302 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.5435779764492534, - "noiseRatio": 1.8234296600891973, - "lowerRatio": 0.9550433290368372, - "upperRatio": 2.4947904424211425 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.5435779764492534, - "noiseRatio": 1.8234296600891973, - "lowerRatio": 0.9550433290368372, - "upperRatio": 2.4947904424211425, - "candidate": [ - 0.09535400000004302, - 0.06094100000018443, - 0.04940499999975145, - 0.054341999999905966, - 0.1158940000000257, - 0.08681000000024142, - 0.08066099999996368 - ], - "baseline": [ - 0.04224400000020978, - 0.03475200000002587, - 0.03346000000010463, - 0.04046100000005026, - 0.057396000000153435, - 0.03976999999986219, - 0.133661000000302 - ], - "selfA": [ - 0.025908999999955995, - 0.06651000000010754, - 0.02683999999999287, - 0.02515799999991941, - 0.022163999999975204, - 0.025247999999919557, - 0.022052999999687017 - ], - "selfB": [ - 0.025328000000172324, - 0.023044000000027154, - 0.022883999999976368, - 0.02295400000002701, - 0.022202999999990425, - 0.02392599999984668, - 0.02228400000012698 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0216255775528418, - "noiseRatio": 2.6635975805854497, - "lowerRatio": 0.5502563143255477, - "upperRatio": 1.8967866311347457, - "candidate": [ - 0.028073000000404136, - 0.03722600000037346, - 0.03188799999998082, - 0.032999999999901775, - 0.031497000000399566, - 0.036203999999997905, - 0.03204800000003161 - ], - "baseline": [ - 0.02642999999989115, - 0.023034999999708816, - 0.05350999999973283, - 0.09242899999981091, - 0.024636999999984255, - 0.01955000000043583, - 0.02385599999979604 - ], - "selfA": [ - 0.08245399999987058, - 0.02698000000009415, - 0.044156999999813706, - 0.031568000000333996, - 0.02613999999994121, - 0.06915399999979854, - 0.021812999999838212 - ], - "selfB": [ - 0.02602999999999156, - 0.05736700000034034, - 0.02724099999977625, - 0.023154999999860593, - 0.03159799999957613, - 0.025989000000208762, - 0.02250400000002628 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.025908999999955995, - 0.06651000000010754, - 0.02683999999999287, - 0.02515799999991941, - 0.022163999999975204, - 0.025247999999919557, - 0.022052999999687017 - ], - "baselineSelfB": [ - 0.025328000000172324, - 0.023044000000027154, - 0.022883999999976368, - 0.02295400000002701, - 0.022202999999990425, - 0.02392599999984668, - 0.02228400000012698 - ] - }, - "historicalReference": { - "A": 5, - "U": 4, - "C": 15, - "plateSwitches": 4, - "commands": [ - [ - 3, - 4 - ], - [ - 1, - -4 - ], - [ - 2, - -2 - ], - [ - 0, - 4 - ], - [ - 1, - -1 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": -4, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "cor-galom-bedroom-chest", - "title": "Cor Galom's bedroom chest", - "optimumA": 14, - "solvers": { - "originalBfs": { - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - [ - 0, - 1 - ], - [ - 1, - -5 - ], - [ - 2, - -5 - ], - [ - 1, - -5 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 3, - 5 - ], - [ - 2, - -5 - ], - [ - 0, - 4 - ], - [ - 1, - -5 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 2, - -3 - ], - [ - 3, - 2 - ] - ], - "samplesMs": [ - 78.03713100000004, - 75.95942600000035, - 70.7323849999998, - 69.93971300000021, - 71.83849899999996, - 70.13433600000008, - 71.32951400000002 - ], - "timingMs": { - "min": 69.93971300000021, - "median": 71.32951400000002, - "p95": 78.03713100000004, - "max": 78.03713100000004 - } - }, - "upstream": { - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - [ - 1, - -3 - ], - [ - 2, - -4 - ], - [ - 1, - -4 - ], - [ - 4, - -4 - ], - [ - 3, - 4 - ], - [ - 1, - -6 - ], - [ - 0, - 2 - ], - [ - 2, - -6 - ], - [ - 1, - -6 - ], - [ - 4, - -6 - ], - [ - 3, - 3 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 32.70353299999988, - 32.28175400000009, - 30.35910799999965, - 30.02255100000002, - 32.019856000000345, - 30.818619000000126, - 32.880349000000024 - ], - "timingMs": { - "min": 30.02255100000002, - "median": 32.019856000000345, - "p95": 32.880349000000024, - "max": 32.880349000000024 - } - }, - "candidate": { - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - [ - 1, - -5 - ], - [ - 2, - -4 - ], - [ - 1, - -4 - ], - [ - 4, - -4 - ], - [ - 1, - -4 - ], - [ - 3, - 4 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 2, - -2 - ], - [ - 1, - -6 - ], - [ - 4, - -6 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.2727210000002742, - 0.23813799999970797, - 0.2338009999998576, - 0.23570500000005268, - 0.26649100000031467, - 0.32682199999999284, - 0.21878900000001522 - ], - "timingMs": { - "min": 0.21878900000001522, - "median": 0.23813799999970797, - "p95": 0.32682199999999284, - "max": 0.32682199999999284 - } - }, - "baseline": { - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - [ - 1, - -5 - ], - [ - 2, - -4 - ], - [ - 1, - -4 - ], - [ - 4, - -4 - ], - [ - 1, - -4 - ], - [ - 3, - 4 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 2, - -2 - ], - [ - 1, - -6 - ], - [ - 4, - -6 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.22145300000011048, - 0.197367000000213, - 0.22184400000014648, - 0.1985580000000482, - 0.19563400000015463, - 0.19846899999993184, - 0.21496300000035262 - ], - "timingMs": { - "min": 0.19563400000015463, - "median": 0.1985580000000482, - "p95": 0.22184400000014648, - "max": 0.22184400000014648 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.2293746761073383, - "noiseRatio": 1.2779273712488457, - "lowerRatio": 1.0375119695781043, - "upperRatio": 1.4567177426093751 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.2293746761073383, - "noiseRatio": 1.2779273712488457, - "lowerRatio": 1.0375119695781043, - "upperRatio": 1.4567177426093751, - "candidate": [ - 0.2727210000002742, - 0.23813799999970797, - 0.2338009999998576, - 0.23570500000005268, - 0.26649100000031467, - 0.32682199999999284, - 0.21878900000001522 - ], - "baseline": [ - 0.22145300000011048, - 0.197367000000213, - 0.22184400000014648, - 0.1985580000000482, - 0.19563400000015463, - 0.19846899999993184, - 0.21496300000035262 - ], - "selfA": [ - 0.18691100000023653, - 0.1442469999997229, - 0.1282629999996061, - 0.12647000000015396, - 0.118228000000272, - 0.20340600000008635, - 0.1367259999997259 - ], - "selfB": [ - 0.16006099999958678, - 0.13411199999973178, - 0.16986500000029991, - 0.1402709999997569, - 0.11820799999986775, - 0.1480229999997391, - 0.11977999999999156 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.2045595890169252, - "noiseRatio": 1.781776198521705, - "lowerRatio": 0.9743331292483921, - "upperRatio": 1.4891865625177991, - "candidate": [ - 0.1674009999996997, - 0.21000599999979386, - 0.17137800000000425, - 0.17270000000007713, - 0.20803299999988667, - 0.17121699999961493, - 0.19240999999965425 - ], - "baseline": [ - 0.14692100000002029, - 0.11700599999994665, - 0.17968999999993684, - 0.1667909999996482, - 0.1608219999998255, - 0.1455789999999979, - 0.16066200000022945 - ], - "selfA": [ - 0.13042700000005425, - 0.35805900000013935, - 0.20953500000041458, - 0.1471510000001217, - 0.1368649999999434, - 0.22306600000001708, - 0.13616499999989173 - ], - "selfB": [ - 0.14216399999986606, - 0.16440700000021025, - 0.18494800000007672, - 0.2198910000001888, - 0.1268910000003416, - 0.12908400000014808, - 0.11905900000010661 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.18691100000023653, - 0.1442469999997229, - 0.1282629999996061, - 0.12647000000015396, - 0.118228000000272, - 0.20340600000008635, - 0.1367259999997259 - ], - "baselineSelfB": [ - 0.16006099999958678, - 0.13411199999973178, - 0.16986500000029991, - 0.1402709999997569, - 0.11820799999986775, - 0.1480229999997391, - 0.11977999999999156 - ] - }, - "historicalReference": { - "A": 14, - "U": 5, - "C": 60, - "plateSwitches": 13, - "commands": [ - [ - 1, - -3 - ], - [ - 2, - -4 - ], - [ - 1, - -4 - ], - [ - 4, - -4 - ], - [ - 3, - 4 - ], - [ - 1, - -6 - ], - [ - 0, - 2 - ], - [ - 2, - -6 - ], - [ - 1, - -6 - ], - [ - 4, - -6 - ], - [ - 3, - 3 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 0, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "swamp-camp-forge-chest", - "title": "Swamp Camp forge chest", - "optimumA": 11, - "solvers": { - "originalBfs": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 2, - 6 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 3, - -5 - ], - [ - 2, - 5 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 1, - -1 - ], - [ - 2, - 5 - ], - [ - 3, - -3 - ], - [ - 4, - -2 - ] - ], - "samplesMs": [ - 106.38693699999976, - 104.2116259999998, - 105.29213999999956, - 120.80871600000046, - 106.04047600000013, - 107.70715899999959, - 107.91578700000082 - ], - "timingMs": { - "min": 104.2116259999998, - "median": 106.38693699999976, - "p95": 120.80871600000046, - "max": 120.80871600000046 - } - }, - "upstream": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 2, - 5 - ], - [ - 3, - -5 - ], - [ - 2, - 6 - ], - [ - 1, - -3 - ], - [ - 5, - -4 - ], - [ - 4, - -2 - ], - [ - 3, - -3 - ], - [ - 2, - 6 - ], - [ - 1, - -2 - ], - [ - 2, - 2 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 42.50637600000027, - 38.003082999999606, - 40.533298000000286, - 37.03745100000015, - 39.681086999999934, - 37.80695100000048, - 37.70319500000005 - ], - "timingMs": { - "min": 37.03745100000015, - "median": 38.003082999999606, - "p95": 42.50637600000027, - "max": 42.50637600000027 - } - }, - "candidate": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 2, - 6 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 3, - -5 - ], - [ - 2, - 5 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 1, - -1 - ], - [ - 2, - 5 - ], - [ - 3, - -3 - ], - [ - 4, - -2 - ] - ], - "samplesMs": [ - 0.2412930000000415, - 0.46133300000019517, - 0.2671220000001995, - 0.319922000000588, - 0.23856900000009773, - 0.2317189999994298, - 0.23393300000043382 - ], - "timingMs": { - "min": 0.2317189999994298, - "median": 0.2412930000000415, - "p95": 0.46133300000019517, - "max": 0.46133300000019517 - } - }, - "baseline": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 2, - 6 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 3, - -5 - ], - [ - 2, - 5 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 1, - -1 - ], - [ - 2, - 5 - ], - [ - 3, - -3 - ], - [ - 4, - -2 - ] - ], - "samplesMs": [ - 0.2088939999998729, - 0.22677100000009887, - 0.21169800000006944, - 0.2113179999996646, - 0.2134909999995216, - 0.24700099999972736, - 0.2110079999993104 - ], - "timingMs": { - "min": 0.2088939999998729, - "median": 0.21169800000006944, - "p95": 0.24700099999972736, - "max": 0.24700099999972736 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.2661667042743199, - "noiseRatio": 1.378466772403809, - "lowerRatio": 0.9695129497313087, - "upperRatio": 1.6535912423420422 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.2661667042743199, - "noiseRatio": 1.378466772403809, - "lowerRatio": 0.9695129497313087, - "upperRatio": 1.6535912423420422, - "candidate": [ - 0.2412930000000415, - 0.46133300000019517, - 0.2671220000001995, - 0.319922000000588, - 0.23856900000009773, - 0.2317189999994298, - 0.23393300000043382 - ], - "baseline": [ - 0.2088939999998729, - 0.22677100000009887, - 0.21169800000006944, - 0.2113179999996646, - 0.2134909999995216, - 0.24700099999972736, - 0.2110079999993104 - ], - "selfA": [ - 0.16586000000006607, - 0.13501299999961702, - 0.12958500000013373, - 0.1888739999999416, - 0.19473299999981464, - 0.15092699999968318, - 0.2803109999999833 - ], - "selfB": [ - 0.14884399999982634, - 0.18413699999973687, - 0.12364600000000792, - 0.15990000000010696, - 0.14521899999999732, - 0.14154300000018338, - 0.20171400000026551 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.1372870802916664, - "noiseRatio": 1.1749239972387453, - "lowerRatio": 1.0019244619522862, - "upperRatio": 1.2909375428144187, - "candidate": [ - 0.18526899999960733, - 0.1995200000001205, - 0.24083299999983865, - 0.21226000000024214, - 0.1744619999999486, - 0.17715700000007928, - 0.16772299999956886 - ], - "baseline": [ - 0.17606499999965308, - 0.16744199999993725, - 0.17738699999972596, - 0.17598499999985506, - 0.18569900000056805, - 0.1473020000003089, - 0.15811800000028597 - ], - "selfA": [ - 0.1477420000001075, - 0.16531799999938812, - 0.17619500000000698, - 0.15789799999947718, - 0.1470209999997678, - 0.1531699999995908, - 0.1494750000001659 - ], - "selfB": [ - 0.1871820000005755, - 0.1607720000001791, - 0.16396699999950215, - 0.1875820000004751, - 0.13090700000066136, - 0.13126800000009098, - 0.1392889999997351 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.16586000000006607, - 0.13501299999961702, - 0.12958500000013373, - 0.1888739999999416, - 0.19473299999981464, - 0.15092699999968318, - 0.2803109999999833 - ], - "baselineSelfB": [ - 0.14884399999982634, - 0.18413699999973687, - 0.12364600000000792, - 0.15990000000010696, - 0.14521899999999732, - 0.14154300000018338, - 0.20171400000026551 - ] - }, - "historicalReference": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 2, - 5 - ], - [ - 3, - -5 - ], - [ - 2, - 6 - ], - [ - 1, - -3 - ], - [ - 5, - -4 - ], - [ - 4, - -2 - ], - [ - 3, - -3 - ], - [ - 2, - 6 - ], - [ - 1, - -2 - ], - [ - 2, - 2 - ], - [ - 0, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "rice-lord-storeroom", - "title": "Rice Lord's storeroom (3rd floor)", - "optimumA": 9, - "solvers": { - "originalBfs": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 1 - ], - [ - 4, - 3 - ], - [ - 2, - 4 - ], - [ - 1, - 2 - ], - [ - 0, - 3 - ], - [ - 3, - 5 - ], - [ - 4, - 3 - ], - [ - 1, - 6 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 9.06322099999943, - 8.184038000000328, - 9.087456999999631, - 7.700109999999768, - 9.050752000000102, - 8.856448999999884, - 8.554274000000078 - ], - "timingMs": { - "min": 7.700109999999768, - "median": 8.856448999999884, - "p95": 9.087456999999631, - "max": 9.087456999999631 - } - }, - "upstream": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 1 - ], - [ - 4, - 3 - ], - [ - 2, - 4 - ], - [ - 1, - 2 - ], - [ - 0, - 3 - ], - [ - 4, - 3 - ], - [ - 3, - 5 - ], - [ - 1, - 6 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 4.752222000000074, - 5.1414080000004105, - 4.72411000000011, - 4.790299999999661, - 4.828517000000829, - 4.838641999999709, - 4.852383000000373 - ], - "timingMs": { - "min": 4.72411000000011, - "median": 4.828517000000829, - "p95": 5.1414080000004105, - "max": 5.1414080000004105 - } - }, - "candidate": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 1 - ], - [ - 4, - 3 - ], - [ - 2, - 4 - ], - [ - 1, - 2 - ], - [ - 0, - 3 - ], - [ - 3, - 5 - ], - [ - 4, - 3 - ], - [ - 1, - 6 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 0.16141300000072079, - 0.13863899999978457, - 0.22659100000055332, - 0.15857799999957933, - 0.13391199999932724, - 0.13361199999962992, - 0.15968100000009144 - ], - "timingMs": { - "min": 0.13361199999962992, - "median": 0.15857799999957933, - "p95": 0.22659100000055332, - "max": 0.22659100000055332 - } - }, - "baseline": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 1 - ], - [ - 4, - 3 - ], - [ - 2, - 4 - ], - [ - 1, - 2 - ], - [ - 0, - 3 - ], - [ - 3, - 5 - ], - [ - 4, - 3 - ], - [ - 1, - 6 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 0.12455700000009529, - 0.1268209999998362, - 0.1233250000004773, - 0.11865899999975227, - 0.12407699999948818, - 0.12579900000037014, - 0.11727699999937613 - ], - "timingMs": { - "min": 0.11727699999937613, - "median": 0.12407699999948818, - "p95": 0.1268209999998362, - "max": 0.1268209999998362 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.2733921934165369, - "noiseRatio": 1.4280139558234128, - "lowerRatio": 1.039203060604052, - "upperRatio": 1.5603569116815743 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.2733921934165369, - "noiseRatio": 1.4280139558234128, - "lowerRatio": 1.039203060604052, - "upperRatio": 1.5603569116815743, - "candidate": [ - 0.16141300000072079, - 0.13863899999978457, - 0.22659100000055332, - 0.15857799999957933, - 0.13391199999932724, - 0.13361199999962992, - 0.15968100000009144 - ], - "baseline": [ - 0.12455700000009529, - 0.1268209999998362, - 0.1233250000004773, - 0.11865899999975227, - 0.12407699999948818, - 0.12579900000037014, - 0.11727699999937613 - ], - "selfA": [ - 0.07259999999951106, - 0.08948500000042259, - 0.0693940000001021, - 0.07046600000012404, - 0.10425699999996141, - 0.06783200000063516, - 0.1344330000001719 - ], - "selfB": [ - 0.07020600000032573, - 0.06903400000010151, - 0.07009500000003754, - 0.06841299999996409, - 0.08381600000029721, - 0.07094700000016019, - 0.07982100000026549 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.258119388779651, - "noiseRatio": 1.5119357617125924, - "lowerRatio": 1.0716904853717635, - "upperRatio": 1.4769790513482033, - "candidate": [ - 0.10513799999989715, - 0.10613999999986845, - 0.2125900000000911, - 0.10628999999971711, - 0.10214400000040769, - 0.10185300000011921, - 0.10141299999941111 - ], - "baseline": [ - 0.10283499999968626, - 0.06833300000016607, - 0.15487300000040705, - 0.09463299999970332, - 0.08783200000016222, - 0.06946500000049127, - 0.08485799999925803 - ], - "selfA": [ - 0.08284399999956804, - 0.0675519999995231, - 0.09455199999956676, - 0.08719099999962054, - 0.07419200000003912, - 0.06814200000007986, - 0.12337600000046223 - ], - "selfB": [ - 0.07309999999961292, - 0.08781199999975797, - 0.1564860000007684, - 0.09386099999937869, - 0.08838300000024901, - 0.08859299999949144, - 0.0846769999998287 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.07259999999951106, - 0.08948500000042259, - 0.0693940000001021, - 0.07046600000012404, - 0.10425699999996141, - 0.06783200000063516, - 0.1344330000001719 - ], - "baselineSelfB": [ - 0.07020600000032573, - 0.06903400000010151, - 0.07009500000003754, - 0.06841299999996409, - 0.08381600000029721, - 0.07094700000016019, - 0.07982100000026549 - ] - }, - "historicalReference": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 1 - ], - [ - 4, - 3 - ], - [ - 2, - 4 - ], - [ - 1, - 2 - ], - [ - 0, - 3 - ], - [ - 4, - 3 - ], - [ - 3, - 5 - ], - [ - 1, - 6 - ], - [ - 0, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "gomez-chambers-door", - "title": "Door to Gomez's chambers", - "optimumA": 9, - "solvers": { - "originalBfs": { - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - [ - 2, - 2 - ], - [ - 1, - -2 - ], - [ - 2, - 2 - ], - [ - 3, - -4 - ], - [ - 0, - -3 - ], - [ - 4, - -4 - ], - [ - 5, - -4 - ], - [ - 3, - -4 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 76.61845400000038, - 77.95652700000028, - 74.67651099999966, - 73.78643199999988, - 76.68536500000027, - 73.76120300000002, - 74.00531200000023 - ], - "timingMs": { - "min": 73.76120300000002, - "median": 74.67651099999966, - "p95": 77.95652700000028, - "max": 77.95652700000028 - } - }, - "upstream": { - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - [ - 2, - 2 - ], - [ - 1, - -2 - ], - [ - 4, - -4 - ], - [ - 3, - -5 - ], - [ - 1, - -3 - ], - [ - 3, - -3 - ], - [ - 5, - -4 - ], - [ - 2, - 2 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 31.29958700000043, - 32.98707699999977, - 31.310483000000204, - 30.83755199999996, - 30.732804000000215, - 31.10607600000003, - 29.940330999999787 - ], - "timingMs": { - "min": 29.940330999999787, - "median": 31.10607600000003, - "p95": 32.98707699999977, - "max": 32.98707699999977 - } - }, - "candidate": { - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - [ - 3, - -3 - ], - [ - 2, - 2 - ], - [ - 1, - -2 - ], - [ - 0, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -4 - ], - [ - 5, - -4 - ], - [ - 3, - -5 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 0.18520899999930407, - 0.18272499999966385, - 0.20560999999997875, - 0.20275500000025204, - 0.2023140000001149, - 0.24476899999990565, - 0.19783800000004703 - ], - "timingMs": { - "min": 0.18272499999966385, - "median": 0.2023140000001149, - "p95": 0.24476899999990565, - "max": 0.24476899999990565 - } - }, - "baseline": { - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - [ - 3, - -3 - ], - [ - 2, - 2 - ], - [ - 1, - -2 - ], - [ - 0, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -4 - ], - [ - 5, - -4 - ], - [ - 3, - -5 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 0.1711780000005092, - 0.23811900000055175, - 0.17112699999961478, - 0.18855399999938527, - 0.1704070000005231, - 0.19064700000035373, - 0.17104699999981676 - ], - "timingMs": { - "min": 0.1704070000005231, - "median": 0.1711780000005092, - "p95": 0.23811900000055175, - "max": 0.23811900000055175 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", - "ratio": 1.0953025797326172, - "noiseRatio": 1.997056279475185, - "lowerRatio": 0.917806297282419, - "upperRatio": 1.307125201386333 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0953025797326172, - "noiseRatio": 1.997056279475185, - "lowerRatio": 0.917806297282419, - "upperRatio": 1.307125201386333, - "candidate": [ - 0.18520899999930407, - 0.18272499999966385, - 0.20560999999997875, - 0.20275500000025204, - 0.2023140000001149, - 0.24476899999990565, - 0.19783800000004703 - ], - "baseline": [ - 0.1711780000005092, - 0.23811900000055175, - 0.17112699999961478, - 0.18855399999938527, - 0.1704070000005231, - 0.19064700000035373, - 0.17104699999981676 - ], - "selfA": [ - 0.1456289999996443, - 0.12461799999982759, - 0.12211399999978312, - 0.12366600000041217, - 0.10585900000023685, - 0.11785800000052404, - 0.10328500000014174 - ], - "selfB": [ - 0.13166799999999057, - 0.1768259999998918, - 0.4124409999994896, - 0.13407100000040373, - 0.09819800000059331, - 0.09606499999972584, - 0.09885900000062975 - ] - }, - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 1.0048653508529528, - "noiseRatio": 1.2318530904862122, - "lowerRatio": 0.8325425916045742, - "upperRatio": 1.2128561151432629, - "candidate": [ - 0.14243400000032125, - 0.14483800000016345, - 0.14515799999935552, - 0.12391699999989214, - 0.11494299999958457, - 0.12594000000080996, - 0.11688599999979488 - ], - "baseline": [ - 0.15070599999944534, - 0.19813799999974435, - 0.13444200000049022, - 0.10912499999994907, - 0.11756700000023557, - 0.09708599999976286, - 0.12137299999994866 - ], - "selfA": [ - 0.13620499999979074, - 0.11972100000002683, - 0.13311999999950785, - 0.12624999999934516, - 0.0984889999999723, - 0.1117180000001099, - 0.13539399999990565 - ], - "selfB": [ - 0.15406099999927392, - 0.15721699999994598, - 0.16378600000007282, - 0.11284999999952561, - 0.0972170000004553, - 0.10681100000056176, - 0.12967500000013388 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.1456289999996443, - 0.12461799999982759, - 0.12211399999978312, - 0.12366600000041217, - 0.10585900000023685, - 0.11785800000052404, - 0.10328500000014174 - ], - "baselineSelfB": [ - 0.13166799999999057, - 0.1768259999998918, - 0.4124409999994896, - 0.13407100000040373, - 0.09819800000059331, - 0.09606499999972584, - 0.09885900000062975 - ] - }, - "historicalReference": { - "A": 9, - "U": 6, - "C": 28, - "plateSwitches": 8, - "commands": [ - [ - 2, - 2 - ], - [ - 1, - -2 - ], - [ - 4, - -4 - ], - [ - 3, - -5 - ], - [ - 1, - -3 - ], - [ - 3, - -3 - ], - [ - 5, - -4 - ], - [ - 2, - 2 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "gomez-throne-hall-chest", - "title": "Chest in Gomez's throne hall", - "optimumA": 5, - "solvers": { - "originalBfs": { - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - [ - 3, - 6 - ], - [ - 0, - 6 - ], - [ - 1, - 2 - ], - [ - 2, - 1 - ], - [ - 3, - 1 - ] - ], - "samplesMs": [ - 0.18502799999987474, - 0.17443199999979697, - 0.17644500000005792, - 0.17631599999913306, - 0.1844169999994847, - 0.22013200000037614, - 0.17918999999983498 - ], - "timingMs": { - "min": 0.17443199999979697, - "median": 0.17918999999983498, - "p95": 0.22013200000037614, - "max": 0.22013200000037614 - } - }, - "upstream": { - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - [ - 3, - 6 - ], - [ - 0, - 6 - ], - [ - 3, - 1 - ], - [ - 2, - 1 - ], - [ - 1, - 2 - ] - ], - "samplesMs": [ - 0.3334820000000036, - 0.2743529999997918, - 0.2259810000005018, - 0.2515979999998308, - 0.23905999999988126, - 0.23649500000010448, - 0.22482899999977235 - ], - "timingMs": { - "min": 0.22482899999977235, - "median": 0.23905999999988126, - "p95": 0.3334820000000036, - "max": 0.3334820000000036 - } - }, - "candidate": { - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - [ - 3, - 6 - ], - [ - 0, - 6 - ], - [ - 1, - 2 - ], - [ - 2, - 1 - ], - [ - 3, - 1 - ] - ], - "samplesMs": [ - 0.02808199999981298, - 0.02301499999975931, - 0.024336999999832187, - 0.029954999999972642, - 0.05179800000041723, - 0.02257400000053167, - 0.02509699999973236 - ], - "timingMs": { - "min": 0.02257400000053167, - "median": 0.02509699999973236, - "p95": 0.05179800000041723, - "max": 0.05179800000041723 - } - }, - "baseline": { - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - [ - 3, - 6 - ], - [ - 0, - 6 - ], - [ - 1, - 2 - ], - [ - 2, - 1 - ], - [ - 3, - 1 - ] - ], - "samplesMs": [ - 0.030535999999301566, - 0.024425999999948544, - 0.025058000000171887, - 0.02899299999990035, - 0.023505000000113796, - 0.022174000000632077, - 0.025397999999768217 - ], - "timingMs": { - "min": 0.022174000000632077, - "median": 0.025058000000171887, - "p95": 0.030535999999301566, - "max": 0.030535999999301566 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.098286972484325, - "noiseRatio": 1.1688702586130604, - "lowerRatio": 0.7935295721410459, - "upperRatio": 1.52008736192932 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.098286972484325, - "noiseRatio": 1.1688702586130604, - "lowerRatio": 0.7935295721410459, - "upperRatio": 1.52008736192932, - "candidate": [ - 0.02808199999981298, - 0.02301499999975931, - 0.024336999999832187, - 0.029954999999972642, - 0.05179800000041723, - 0.02257400000053167, - 0.02509699999973236 - ], - "baseline": [ - 0.030535999999301566, - 0.024425999999948544, - 0.025058000000171887, - 0.02899299999990035, - 0.023505000000113796, - 0.022174000000632077, - 0.025397999999768217 - ], - "selfA": [ - 0.015903999999864027, - 0.01553299999977753, - 0.015212000000246917, - 0.015603000000737666, - 0.015753000000586326, - 0.015693000000283064, - 0.01557299999967654 - ], - "selfB": [ - 0.01758700000027602, - 0.015913999999611406, - 0.015473999999812804, - 0.01727599999958329, - 0.01535299999977724, - 0.017126000000644126, - 0.019268999999439984 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.8984484274970228, - "noiseRatio": 1.3836720164698226, - "lowerRatio": 0.555209174368421, - "upperRatio": 1.4538837147100736, - "candidate": [ - 0.016765000000305008, - 0.01981900000009773, - 0.01693599999998696, - 0.01614400000016758, - 0.023946000000250933, - 0.02127199999995355, - 0.018207000000074913 - ], - "baseline": [ - 0.015754000000015367, - 0.04057100000045466, - 0.017717000000629923, - 0.03558399999928952, - 0.019710000000486616, - 0.014962999999625026, - 0.015002999999524036 - ], - "selfA": [ - 0.017295999999987544, - 0.016775999999481428, - 0.018216999999822292, - 0.029314000000340457, - 0.016695000000254367, - 0.014983000000029278, - 0.01515300000028219 - ], - "selfB": [ - 0.01573400000052061, - 0.01626399999986461, - 0.015903999999864027, - 0.01723599999968428, - 0.015223000000332831, - 0.01785699999982171, - 0.017406999999366235 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.015903999999864027, - 0.01553299999977753, - 0.015212000000246917, - 0.015603000000737666, - 0.015753000000586326, - 0.015693000000283064, - 0.01557299999967654 - ], - "baselineSelfB": [ - 0.01758700000027602, - 0.015913999999611406, - 0.015473999999812804, - 0.01727599999958329, - 0.01535299999977724, - 0.017126000000644126, - 0.019268999999439984 - ] - }, - "historicalReference": { - "A": 5, - "U": 4, - "C": 16, - "plateSwitches": 4, - "commands": [ - [ - 3, - 6 - ], - [ - 0, - 6 - ], - [ - 3, - 1 - ], - [ - 2, - 1 - ], - [ - 1, - 2 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "old-camp-dungeon-door", - "title": "Dungeon door by the executed ghost", - "optimumA": 17, - "solvers": { - "originalBfs": { - "A": 17, - "U": 6, - "C": 72, - "plateSwitches": 16, - "commands": [ - [ - 3, - 2 - ], - [ - 4, - -2 - ], - [ - 1, - -1 - ], - [ - 2, - -4 - ], - [ - 0, - -5 - ], - [ - 5, - -6 - ], - [ - 4, - -5 - ], - [ - 5, - -5 - ], - [ - 2, - -6 - ], - [ - 0, - -6 - ], - [ - 1, - -6 - ], - [ - 0, - -6 - ], - [ - 3, - -3 - ], - [ - 5, - -6 - ], - [ - 2, - -3 - ], - [ - 4, - -3 - ], - [ - 5, - -3 - ] - ], - "samplesMs": [ - 90.52278000000024, - 87.66105399999924, - 90.33665099999962, - 92.7997919999998, - 91.53709700000036, - 125.40649699999994, - 90.29265399999986 - ], - "timingMs": { - "min": 87.66105399999924, - "median": 90.52278000000024, - "p95": 125.40649699999994, - "max": 125.40649699999994 - } - }, - "upstream": { - "A": 18, - "U": 6, - "C": 68, - "plateSwitches": 17, - "commands": [ - [ - 4, - -3 - ], - [ - 2, - -3 - ], - [ - 5, - -6 - ], - [ - 0, - -3 - ], - [ - 1, - -3 - ], - [ - 2, - -3 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 4, - -6 - ], - [ - 5, - -6 - ], - [ - 2, - -5 - ], - [ - 5, - -5 - ], - [ - 0, - -5 - ], - [ - 1, - -4 - ], - [ - 4, - -1 - ], - [ - 3, - -1 - ], - [ - 2, - -2 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 49.4057929999999, - 44.25354399999924, - 42.91580999999951, - 41.27184600000055, - 41.655573000000004, - 42.125340000000506, - 42.866986999999426 - ], - "timingMs": { - "min": 41.27184600000055, - "median": 42.866986999999426, - "p95": 49.4057929999999, - "max": 49.4057929999999 - } - }, - "candidate": { - "A": 17, - "U": 6, - "C": 72, - "plateSwitches": 16, - "commands": [ - [ - 4, - -2 - ], - [ - 1, - -1 - ], - [ - 3, - 2 - ], - [ - 2, - -4 - ], - [ - 5, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - -5 - ], - [ - 5, - -6 - ], - [ - 2, - -6 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 1, - -6 - ], - [ - 0, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ], - [ - 5, - -6 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 2.243797000000086, - 2.1073319999995874, - 2.8227689999994254, - 2.0736109999998007, - 2.4010239999997793, - 2.1424639999995634, - 2.2628960000001825 - ], - "timingMs": { - "min": 2.0736109999998007, - "median": 2.243797000000086, - "p95": 2.8227689999994254, - "max": 2.8227689999994254 - } - }, - "baseline": { - "A": 17, - "U": 6, - "C": 72, - "plateSwitches": 16, - "commands": [ - [ - 4, - -2 - ], - [ - 1, - -1 - ], - [ - 3, - 2 - ], - [ - 2, - -4 - ], - [ - 5, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - -5 - ], - [ - 5, - -6 - ], - [ - 2, - -6 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 1, - -6 - ], - [ - 0, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ], - [ - 5, - -6 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 2.316385999999511, - 2.3156350000008388, - 2.279461000000083, - 2.2497960000000603, - 2.2515990000001693, - 2.1491150000001653, - 2.3162560000000667 - ], - "timingMs": { - "min": 2.1491150000001653, - "median": 2.279461000000083, - "p95": 2.316385999999511, - "max": 2.316385999999511 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", - "ratio": 1.006302605015658, - "noiseRatio": 1.0354971548980059, - "lowerRatio": 0.9009490475724129, - "upperRatio": 1.1239758070557362 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.006302605015658, - "noiseRatio": 1.0354971548980059, - "lowerRatio": 0.9009490475724129, - "upperRatio": 1.1239758070557362, - "candidate": [ - 2.243797000000086, - 2.1073319999995874, - 2.8227689999994254, - 2.0736109999998007, - 2.4010239999997793, - 2.1424639999995634, - 2.2628960000001825 - ], - "baseline": [ - 2.316385999999511, - 2.3156350000008388, - 2.279461000000083, - 2.2497960000000603, - 2.2515990000001693, - 2.1491150000001653, - 2.3162560000000667 - ], - "selfA": [ - 2.163226000000577, - 2.1978369999997085, - 2.169014000000061, - 2.1042370000004667, - 2.130677000000105, - 2.083144999999604, - 2.153741999999511 - ], - "selfB": [ - 2.199239999999918, - 2.1076819999998406, - 2.173370999999861, - 2.224457999999686, - 2.088564000000588, - 2.1182980000003226, - 2.0986880000000383 - ] - }, - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.9176887142504068, - "noiseRatio": 1.0929010914639148, - "lowerRatio": 0.8009103285317423, - "upperRatio": 1.0514942138484207, - "candidate": [ - 2.4204629999994722, - 2.274282999999741, - 2.3416550000001735, - 2.299149999999827, - 2.084907999999814, - 2.043265000000247, - 2.107111000000259 - ], - "baseline": [ - 2.6924419999995735, - 2.2727209999993647, - 2.285029000000577, - 3.306525999999394, - 2.162405000000035, - 2.1541919999999664, - 2.2536520000003293 - ], - "selfA": [ - 2.6162569999996776, - 2.4628169999996317, - 2.1627150000003894, - 2.1810230000000956, - 2.1425349999999526, - 2.0998510000008537, - 2.1898160000000644 - ], - "selfB": [ - 2.4278629999998884, - 2.4364980000000287, - 2.179070000000138, - 2.544639000000643, - 2.1554740000001402, - 2.168913999999859, - 2.1863809999995283 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 2.163226000000577, - 2.1978369999997085, - 2.169014000000061, - 2.1042370000004667, - 2.130677000000105, - 2.083144999999604, - 2.153741999999511 - ], - "baselineSelfB": [ - 2.199239999999918, - 2.1076819999998406, - 2.173370999999861, - 2.224457999999686, - 2.088564000000588, - 2.1182980000003226, - 2.0986880000000383 - ] - }, - "historicalReference": { - "A": 18, - "U": 6, - "C": 68, - "plateSwitches": 17, - "commands": [ - [ - 4, - -3 - ], - [ - 2, - -3 - ], - [ - 5, - -6 - ], - [ - 0, - -3 - ], - [ - 1, - -3 - ], - [ - 2, - -3 - ], - [ - 5, - -3 - ], - [ - 0, - -6 - ], - [ - 4, - -6 - ], - [ - 5, - -6 - ], - [ - 2, - -5 - ], - [ - 5, - -5 - ], - [ - 0, - -5 - ], - [ - 1, - -4 - ], - [ - 4, - -1 - ], - [ - 3, - -1 - ], - [ - 2, - -2 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "regression", - "deltas": { - "A": -1, - "U": 0, - "C": 4, - "plateSwitches": -1 - }, - "regressions": [ - "C" - ] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "arlin-dungeon-key", - "title": "Arlin's dungeon key", - "optimumA": 7, - "solvers": { - "originalBfs": { - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - [ - 2, - -2 - ], - [ - 5, - -2 - ], - [ - 6, - -6 - ], - [ - 0, - -3 - ], - [ - 2, - -6 - ], - [ - 1, - 3 - ], - [ - 5, - -3 - ] - ], - "samplesMs": [ - 49.07597900000019, - 48.175131999999394, - 50.91433400000005, - 48.47274000000016, - 47.77182500000072, - 46.63624599999912, - 49.09639799999968 - ], - "timingMs": { - "min": 46.63624599999912, - "median": 48.47274000000016, - "p95": 50.91433400000005, - "max": 50.91433400000005 - } - }, - "upstream": { - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - [ - 2, - -2 - ], - [ - 6, - -6 - ], - [ - 2, - -3 - ], - [ - 5, - -5 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 21.109174000000166, - 21.5032369999999, - 21.403597000000445, - 21.04023100000086, - 21.470196999999644, - 21.44747400000051, - 20.977767000000313 - ], - "timingMs": { - "min": 20.977767000000313, - "median": 21.403597000000445, - "p95": 21.5032369999999, - "max": 21.5032369999999 - } - }, - "candidate": { - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - [ - 2, - -2 - ], - [ - 5, - -2 - ], - [ - 6, - -6 - ], - [ - 0, - -3 - ], - [ - 2, - -6 - ], - [ - 1, - 3 - ], - [ - 5, - -3 - ] - ], - "samplesMs": [ - 7.786336000000119, - 7.5857050000004165, - 7.949791999999434, - 11.08267700000033, - 8.164826000000176, - 8.045687000001635, - 7.698093000000881 - ], - "timingMs": { - "min": 7.5857050000004165, - "median": 7.949791999999434, - "p95": 11.08267700000033, - "max": 11.08267700000033 - } - }, - "baseline": { - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - [ - 2, - -2 - ], - [ - 5, - -2 - ], - [ - 6, - -6 - ], - [ - 0, - -3 - ], - [ - 2, - -6 - ], - [ - 1, - 3 - ], - [ - 5, - -3 - ] - ], - "samplesMs": [ - 8.873804999999265, - 7.581557999999859, - 9.488448000000062, - 11.834627999999611, - 7.734927999999854, - 7.649639999999636, - 8.985781000001225 - ], - "timingMs": { - "min": 7.581557999999859, - "median": 8.873804999999265, - "p95": 11.834627999999611, - "max": 11.834627999999611 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.9413789915267663, - "noiseRatio": 1.3054522689394972, - "lowerRatio": 0.8502056094171313, - "upperRatio": 1.042329521085485 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9413789915267663, - "noiseRatio": 1.3054522689394972, - "lowerRatio": 0.8502056094171313, - "upperRatio": 1.042329521085485, - "candidate": [ - 7.786336000000119, - 7.5857050000004165, - 7.949791999999434, - 11.08267700000033, - 8.164826000000176, - 8.045687000001635, - 7.698093000000881 - ], - "baseline": [ - 8.873804999999265, - 7.581557999999859, - 9.488448000000062, - 11.834627999999611, - 7.734927999999854, - 7.649639999999636, - 8.985781000001225 - ], - "selfA": [ - 9.020376000000397, - 8.74142700000084, - 8.722897999999986, - 14.555786999999327, - 9.61799500000052, - 8.00543899999957, - 8.643188999999438 - ], - "selfB": [ - 8.655327000000398, - 7.866339000000153, - 8.522515999999996, - 9.86421700000028, - 8.223336999999447, - 9.90675100000044, - 8.28538000000026 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.02890269964583, - "noiseRatio": 1.2233811416595932, - "lowerRatio": 0.7594313844191403, - "upperRatio": 1.3939913296422302, - "candidate": [ - 9.4491280000002, - 10.120609000001423, - 8.276654000001145, - 10.115431000000171, - 7.68337099999917, - 10.484907000000021, - 7.981069999999818 - ], - "baseline": [ - 12.687918000001446, - 7.484291999999186, - 12.554842000001372, - 7.611973999999464, - 7.567999000000782, - 7.9831429999994725, - 7.691133000000264 - ], - "selfA": [ - 7.760707999999795, - 7.557883000001311, - 7.572875000001659, - 7.514137000000119, - 7.608208999999988, - 11.387746000000334, - 7.910373000000618 - ], - "selfB": [ - 7.76864999999998, - 7.878064999998969, - 7.5173610000001645, - 7.664923999998791, - 7.647297000001345, - 7.85381899999993, - 7.573656999999002 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 9.020376000000397, - 8.74142700000084, - 8.722897999999986, - 14.555786999999327, - 9.61799500000052, - 8.00543899999957, - 8.643188999999438 - ], - "baselineSelfB": [ - 8.655327000000398, - 7.866339000000153, - 8.522515999999996, - 9.86421700000028, - 8.223336999999447, - 9.90675100000044, - 8.28538000000026 - ] - }, - "historicalReference": { - "A": 7, - "U": 5, - "C": 25, - "plateSwitches": 6, - "commands": [ - [ - 2, - -2 - ], - [ - 6, - -6 - ], - [ - 2, - -3 - ], - [ - 5, - -5 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "gomez-wine-cellar", - "title": "Gomez's wine cellar", - "optimumA": 9, - "solvers": { - "originalBfs": { - "A": 9, - "U": 6, - "C": 41, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 2, - 5 - ], - [ - 5, - 4 - ], - [ - 3, - 4 - ], - [ - 2, - -6 - ], - [ - 4, - -5 - ], - [ - 3, - 6 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 59.59877199999937, - 59.65067900000031, - 57.79490699999951, - 56.794411999999284, - 57.24158499999976, - 60.53570200000104, - 73.16609699999935 - ], - "timingMs": { - "min": 56.794411999999284, - "median": 59.59877199999937, - "p95": 73.16609699999935, - "max": 73.16609699999935 - } - }, - "upstream": { - "A": 11, - "U": 6, - "C": 31, - "plateSwitches": 10, - "commands": [ - [ - 3, - 1 - ], - [ - 4, - -2 - ], - [ - 3, - 2 - ], - [ - 5, - 4 - ], - [ - 3, - 4 - ], - [ - 4, - -6 - ], - [ - 2, - -1 - ], - [ - 3, - 3 - ], - [ - 4, - -3 - ], - [ - 1, - -2 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 27.881734999999026, - 27.45158899999842, - 28.345423000000665, - 27.98457000000053, - 30.36988999999994, - 28.340355000000272, - 29.158136000000013 - ], - "timingMs": { - "min": 27.45158899999842, - "median": 28.340355000000272, - "p95": 30.36988999999994, - "max": 30.36988999999994 - } - }, - "candidate": { - "A": 9, - "U": 6, - "C": 41, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 2, - 5 - ], - [ - 5, - 4 - ], - [ - 3, - 4 - ], - [ - 2, - -6 - ], - [ - 4, - -5 - ], - [ - 3, - 6 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 0.1863999999986845, - 0.1045670000003156, - 0.11899900000025809, - 0.11747699999978067, - 0.11929000000054657, - 0.1116280000005645, - 0.22822400000040943 - ], - "timingMs": { - "min": 0.1045670000003156, - "median": 0.11899900000025809, - "p95": 0.22822400000040943, - "max": 0.22822400000040943 - } - }, - "baseline": { - "A": 9, - "U": 6, - "C": 41, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 2, - 5 - ], - [ - 5, - 4 - ], - [ - 3, - 4 - ], - [ - 2, - -6 - ], - [ - 4, - -5 - ], - [ - 3, - 6 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 0.09228900000016438, - 0.08511799999905634, - 0.08879399999932502, - 0.10961499999939406, - 0.1684640000003128, - 0.08565900000030524, - 0.09263000000100874 - ], - "timingMs": { - "min": 0.08511799999905634, - "median": 0.09228900000016438, - "p95": 0.1684640000003128, - "max": 0.1684640000003128 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.3483500011099405, - "noiseRatio": 1.6646882648812724, - "lowerRatio": 0.8777996584971208, - "upperRatio": 2.0711419831329763 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.3483500011099405, - "noiseRatio": 1.6646882648812724, - "lowerRatio": 0.8777996584971208, - "upperRatio": 2.0711419831329763, - "candidate": [ - 0.1863999999986845, - 0.1045670000003156, - 0.11899900000025809, - 0.11747699999978067, - 0.11929000000054657, - 0.1116280000005645, - 0.22822400000040943 - ], - "baseline": [ - 0.09228900000016438, - 0.08511799999905634, - 0.08879399999932502, - 0.10961499999939406, - 0.1684640000003128, - 0.08565900000030524, - 0.09263000000100874 - ], - "selfA": [ - 0.12335500000153843, - 0.11847799999850395, - 0.11239899999964109, - 0.1932010000000446, - 0.13438200000018696, - 0.054151999998794054, - 0.05409200000030978 - ], - "selfB": [ - 0.14424700000017765, - 0.12425599999914994, - 0.1141219999990426, - 0.11610400000063237, - 0.06396700000004785, - 0.055883999999423395, - 0.048773000000437605 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.8996419177963317, - "noiseRatio": 2.033179562713795, - "lowerRatio": 1.091985258245267, - "upperRatio": 3.304659461839001, - "candidate": [ - 0.07019500000023982, - 0.06825199999912002, - 0.07229900000129419, - 0.3428249999997206, - 0.1518579999992653, - 0.13267000000087137, - 0.06638000000020838 - ], - "baseline": [ - 0.061402999999700114, - 0.047902000000249245, - 0.049924999999348074, - 0.06497800000033749, - 0.06417700000019977, - 0.05796799999916402, - 0.050116000000343774 - ], - "selfA": [ - 0.05384100000082981, - 0.044447000000218395, - 0.04970499999944877, - 0.05606499999885273, - 0.04966499999864027, - 0.05872799999997369, - 0.06912499999998545 - ], - "selfB": [ - 0.05457300000125542, - 0.043426000000181375, - 0.0860290000000532, - 0.0498250000000553, - 0.14181300000018382, - 0.08713200000056531, - 0.058007999999972526 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.12335500000153843, - 0.11847799999850395, - 0.11239899999964109, - 0.1932010000000446, - 0.13438200000018696, - 0.054151999998794054, - 0.05409200000030978 - ], - "baselineSelfB": [ - 0.14424700000017765, - 0.12425599999914994, - 0.1141219999990426, - 0.11610400000063237, - 0.06396700000004785, - 0.055883999999423395, - 0.048773000000437605 - ] - }, - "historicalReference": { - "A": 11, - "U": 6, - "C": 31, - "plateSwitches": 10, - "commands": [ - [ - 3, - 1 - ], - [ - 4, - -2 - ], - [ - 3, - 2 - ], - [ - 5, - 4 - ], - [ - 3, - 4 - ], - [ - 4, - -6 - ], - [ - 2, - -1 - ], - [ - 3, - 3 - ], - [ - 4, - -3 - ], - [ - 1, - -2 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "regression", - "deltas": { - "A": -2, - "U": 0, - "C": 10, - "plateSwitches": -2 - }, - "regressions": [ - "C" - ] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "gomez-bedside-chest", - "title": "Gomez bedside chest", - "optimumA": 23, - "solvers": { - "originalBfs": { - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 1, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 4, - 1 - ], - [ - 2, - -1 - ], - [ - 0, - -2 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ], - [ - 1, - -3 - ], - [ - 4, - 3 - ], - [ - 2, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - -6 - ], - [ - 1, - -6 - ], - [ - 3, - -5 - ], - [ - 4, - 3 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - 3 - ], - [ - 1, - -6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 10.746721000001344, - 10.780200999999579, - 10.484566999999515, - 11.635549000000537, - 11.074744999999893, - 10.778028000000631, - 10.780791999999565 - ], - "timingMs": { - "min": 10.484566999999515, - "median": 10.780200999999579, - "p95": 11.635549000000537, - "max": 11.635549000000537 - } - }, - "upstream": { - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - [ - 2, - -1 - ], - [ - 1, - -1 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -4 - ], - [ - 4, - 2 - ], - [ - 0, - -2 - ], - [ - 2, - -2 - ], - [ - 1, - -4 - ], - [ - 0, - -6 - ], - [ - 1, - -6 - ], - [ - 4, - 3 - ], - [ - 3, - -5 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - 3 - ], - [ - 1, - -6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 10.757768000001306, - 8.769925999999032, - 8.463854999999967, - 8.778299999999945, - 9.44001500000013, - 8.635173000000577, - 8.045016000000032 - ], - "timingMs": { - "min": 8.045016000000032, - "median": 8.769925999999032, - "p95": 10.757768000001306, - "max": 10.757768000001306 - } - }, - "candidate": { - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - [ - 2, - -1 - ], - [ - 1, - -1 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -4 - ], - [ - 0, - -4 - ], - [ - 1, - -4 - ], - [ - 4, - 2 - ], - [ - 2, - -2 - ], - [ - 3, - -5 - ], - [ - 0, - -4 - ], - [ - 1, - -6 - ], - [ - 4, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - 3 - ], - [ - 1, - -6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 1.8123070000001462, - 1.4463869999999588, - 1.5601980000010371, - 2.550982999999178, - 1.6436040000007779, - 1.4755199999999604, - 1.7677700000003824 - ], - "timingMs": { - "min": 1.4463869999999588, - "median": 1.6436040000007779, - "p95": 2.550982999999178, - "max": 2.550982999999178 - } - }, - "baseline": { - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - [ - 2, - -1 - ], - [ - 1, - -1 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -4 - ], - [ - 0, - -4 - ], - [ - 1, - -4 - ], - [ - 4, - 2 - ], - [ - 2, - -2 - ], - [ - 3, - -5 - ], - [ - 0, - -4 - ], - [ - 1, - -6 - ], - [ - 4, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - 3 - ], - [ - 1, - -6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 1.5166619999999966, - 1.5091409999986354, - 1.6094819999998435, - 2.3969969999998284, - 1.5684000000001106, - 1.8698530000001483, - 1.557993999998871 - ], - "timingMs": { - "min": 1.5091409999986354, - "median": 1.5684000000001106, - "p95": 2.3969969999998284, - "max": 2.3969969999998284 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", - "ratio": 1.014835411222671, - "noiseRatio": 1.4941160366628865, - "lowerRatio": 0.8798147108014918, - "upperRatio": 1.1705770535858397 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.014835411222671, - "noiseRatio": 1.4941160366628865, - "lowerRatio": 0.8798147108014918, - "upperRatio": 1.1705770535858397, - "candidate": [ - 1.8123070000001462, - 1.4463869999999588, - 1.5601980000010371, - 2.550982999999178, - 1.6436040000007779, - 1.4755199999999604, - 1.7677700000003824 - ], - "baseline": [ - 1.5166619999999966, - 1.5091409999986354, - 1.6094819999998435, - 2.3969969999998284, - 1.5684000000001106, - 1.8698530000001483, - 1.557993999998871 - ], - "selfA": [ - 1.7827120000001742, - 1.4631010000011884, - 1.7880009999989852, - 1.5858869999992748, - 1.545405999999275, - 1.559917000000496, - 3.7770029999992403 - ], - "selfB": [ - 1.491173999998864, - 1.5276580000008835, - 1.571364999999787, - 1.5719850000004953, - 1.533116999999038, - 2.1329100000002654, - 1.949663999999757 - ] - }, - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 1.0168485077623741, - "noiseRatio": 1.2009298393321017, - "lowerRatio": 0.8635720868931483, - "upperRatio": 1.1973301400448153, - "candidate": [ - 2.2693949999993492, - 1.72632799999883, - 1.4953810000006342, - 1.441408999999112, - 1.447018000000753, - 1.622201000000132, - 1.5337479999998322 - ], - "baseline": [ - 1.611895000000004, - 1.812448000000586, - 1.4912939999994705, - 1.6364419999990787, - 1.5943889999998646, - 1.5431419999986247, - 1.5418599999993603 - ], - "selfA": [ - 1.4916639999992185, - 1.953589999999167, - 1.509001000000353, - 2.0842869999996765, - 1.947900999999547, - 1.8309650000010151, - 1.5705930000003718 - ], - "selfB": [ - 1.6326559999997698, - 1.6040740000007645, - 1.4744590000009339, - 1.6683310000007623, - 1.852548000000752, - 1.7716760000002978, - 1.4839430000010907 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 1.7827120000001742, - 1.4631010000011884, - 1.7880009999989852, - 1.5858869999992748, - 1.545405999999275, - 1.559917000000496, - 3.7770029999992403 - ], - "baselineSelfB": [ - 1.491173999998864, - 1.5276580000008835, - 1.571364999999787, - 1.5719850000004953, - 1.533116999999038, - 2.1329100000002654, - 1.949663999999757 - ] - }, - "historicalReference": { - "A": 23, - "U": 5, - "C": 68, - "plateSwitches": 22, - "commands": [ - [ - 2, - -1 - ], - [ - 1, - -1 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - -4 - ], - [ - 4, - 2 - ], - [ - 0, - -2 - ], - [ - 2, - -2 - ], - [ - 1, - -4 - ], - [ - 0, - -6 - ], - [ - 1, - -6 - ], - [ - 4, - 3 - ], - [ - 3, - -5 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - 3 - ], - [ - 1, - -6 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "gomez-wall-chest", - "title": "Wall chest in Gomez's chambers", - "optimumA": 20, - "solvers": { - "originalBfs": { - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - [ - 0, - -2 - ], - [ - 3, - -2 - ], - [ - 4, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 5, - -6 - ], - [ - 0, - -5 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 3, - -4 - ], - [ - 5, - -5 - ], - [ - 0, - -2 - ], - [ - 2, - 4 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ], - [ - 1, - -3 - ], - [ - 5, - -2 - ] - ], - "samplesMs": [ - 94.44142199999987, - 95.14554099999987, - 103.42940699999963, - 108.77189499999986, - 102.2110130000001, - 101.36178499999914, - 95.80923000000075 - ], - "timingMs": { - "min": 94.44142199999987, - "median": 101.36178499999914, - "p95": 108.77189499999986, - "max": 108.77189499999986 - } - }, - "upstream": { - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - [ - 0, - -2 - ], - [ - 4, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ], - [ - 5, - -6 - ], - [ - 3, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 0, - -5 - ], - [ - 5, - -5 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 3, - -3 - ], - [ - 0, - -2 - ], - [ - 5, - -2 - ], - [ - 2, - 4 - ], - [ - 4, - -6 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 88.82958700000017, - 87.62570099999903, - 104.45869599999969, - 80.36745399999927, - 83.08285000000069, - 84.6270940000013, - 89.74200000000019 - ], - "timingMs": { - "min": 80.36745399999927, - "median": 87.62570099999903, - "p95": 104.45869599999969, - "max": 104.45869599999969 - } - }, - "candidate": { - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - [ - 0, - -2 - ], - [ - 4, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ], - [ - 5, - -6 - ], - [ - 3, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 3, - -3 - ], - [ - 5, - -5 - ], - [ - 0, - -2 - ], - [ - 5, - -2 - ], - [ - 2, - 4 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 5.743753000000652, - 7.4569809999993595, - 5.275819000000411, - 6.521903999999267, - 6.599210000000312, - 5.40407100000084, - 5.326575999999477 - ], - "timingMs": { - "min": 5.275819000000411, - "median": 5.743753000000652, - "p95": 7.4569809999993595, - "max": 7.4569809999993595 - } - }, - "baseline": { - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - [ - 0, - -2 - ], - [ - 4, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ], - [ - 5, - -6 - ], - [ - 3, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 3, - -3 - ], - [ - 5, - -5 - ], - [ - 0, - -2 - ], - [ - 5, - -2 - ], - [ - 2, - 4 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 10.002911999999924, - 5.486915999999837, - 6.602254999999786, - 5.618484999999055, - 7.515418999999383, - 5.631263999999646, - 5.674248999999691 - ], - "timingMs": { - "min": 5.486915999999837, - "median": 5.674248999999691, - "p95": 10.002911999999924, - "max": 10.002911999999924 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", - "ratio": 0.9234368622156773, - "noiseRatio": 1.191579482731704, - "lowerRatio": 0.6922393548573168, - "upperRatio": 1.2318508511763249 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9234368622156773, - "noiseRatio": 1.191579482731704, - "lowerRatio": 0.6922393548573168, - "upperRatio": 1.2318508511763249, - "candidate": [ - 5.743753000000652, - 7.4569809999993595, - 5.275819000000411, - 6.521903999999267, - 6.599210000000312, - 5.40407100000084, - 5.326575999999477 - ], - "baseline": [ - 10.002911999999924, - 5.486915999999837, - 6.602254999999786, - 5.618484999999055, - 7.515418999999383, - 5.631263999999646, - 5.674248999999691 - ], - "selfA": [ - 6.268602000000101, - 5.488078000000314, - 5.292864999999438, - 7.186845000000176, - 5.845555999998396, - 5.5085889999991196, - 5.3529750000016065 - ], - "selfB": [ - 5.99640300000101, - 5.394156999998813, - 5.97029399999883, - 5.341488000000027, - 5.341818000000785, - 5.774810000000798, - 5.59995600000002 - ] - }, - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 1.0206785237529408, - "noiseRatio": 1.1590310329068552, - "lowerRatio": 0.9222803904271297, - "upperRatio": 1.12957475802777, - "candidate": [ - 5.9965529999990395, - 6.344356999999945, - 5.378031999998711, - 5.416981999998825, - 5.530692000000272, - 5.256609000000026, - 5.45744200000081 - ], - "baseline": [ - 5.416751000000659, - 5.263990999999805, - 5.859156000000439, - 5.325683999999455, - 5.595610000000306, - 5.674387999999453, - 5.393747000000076 - ], - "selfA": [ - 5.605606000000989, - 5.598875000001499, - 5.652105000001029, - 5.572736000000077, - 5.607557999999699, - 5.301447999998345, - 5.431673000000956 - ], - "selfB": [ - 5.335167999999612, - 5.529191000001447, - 5.311192000001029, - 5.4573920000002545, - 5.262998999998672, - 7.140935999999783, - 5.447738000000754 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 6.268602000000101, - 5.488078000000314, - 5.292864999999438, - 7.186845000000176, - 5.845555999998396, - 5.5085889999991196, - 5.3529750000016065 - ], - "baselineSelfB": [ - 5.99640300000101, - 5.394156999998813, - 5.97029399999883, - 5.341488000000027, - 5.341818000000785, - 5.774810000000798, - 5.59995600000002 - ] - }, - "historicalReference": { - "A": 20, - "U": 6, - "C": 75, - "plateSwitches": 19, - "commands": [ - [ - 0, - -2 - ], - [ - 4, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ], - [ - 5, - -6 - ], - [ - 3, - -3 - ], - [ - 2, - 2 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 0, - -5 - ], - [ - 5, - -5 - ], - [ - 4, - -5 - ], - [ - 1, - -5 - ], - [ - 3, - -3 - ], - [ - 0, - -2 - ], - [ - 5, - -2 - ], - [ - 2, - 4 - ], - [ - 4, - -6 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "voran-bedside-chest", - "title": "Voran's bedside chest", - "optimumA": 9, - "solvers": { - "originalBfs": { - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 4, - -1 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 3, - 6 - ], - [ - 5, - 6 - ], - [ - 3, - 6 - ], - [ - 6, - -5 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 199.1664579999997, - 190.00821799999903, - 208.4962169999999, - 197.6131600000008, - 198.21367400000054, - 217.74354200000016, - 204.622268000001 - ], - "timingMs": { - "min": 190.00821799999903, - "median": 199.1664579999997, - "p95": 217.74354200000016, - "max": 217.74354200000016 - } - }, - "upstream": { - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - [ - 4, - -1 - ], - [ - 6, - -5 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 3, - 6 - ], - [ - 5, - 6 - ], - [ - 3, - 6 - ], - [ - 4, - -3 - ], - [ - 1, - -2 - ] - ], - "samplesMs": [ - 178.98545000000013, - 180.83803799999987, - 188.3009579999998, - 184.55181599999923, - 196.82584599999973, - 192.62150900000051, - 219.23769600000014 - ], - "timingMs": { - "min": 178.98545000000013, - "median": 188.3009579999998, - "p95": 219.23769600000014, - "max": 219.23769600000014 - } - }, - "candidate": { - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 4, - -1 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 3, - 6 - ], - [ - 5, - 6 - ], - [ - 3, - 6 - ], - [ - 6, - -5 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 0.11362100000042119, - 0.1842980000001262, - 0.11203899999964051, - 0.11047600000165403, - 0.11176900000100431, - 0.11115699999936623, - 0.1573370000005525 - ], - "timingMs": { - "min": 0.11047600000165403, - "median": 0.11203899999964051, - "p95": 0.1842980000001262, - "max": 0.1842980000001262 - } - }, - "baseline": { - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - [ - 1, - -2 - ], - [ - 4, - -1 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 3, - 6 - ], - [ - 5, - 6 - ], - [ - 3, - 6 - ], - [ - 6, - -5 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 0.36556000000018685, - 0.16793300000063027, - 0.09411200000067765, - 0.09358099999917613, - 0.12914400000045134, - 0.09942899999987276, - 0.5305889999999636 - ], - "timingMs": { - "min": 0.09358099999917613, - "median": 0.12914400000045134, - "p95": 0.5305889999999636, - "max": 0.5305889999999636 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.7532149439120852, - "noiseRatio": 1.1275293681150074, - "lowerRatio": 0.3889327336371895, - "upperRatio": 1.4586911891600107 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.7532149439120852, - "noiseRatio": 1.1275293681150074, - "lowerRatio": 0.3889327336371895, - "upperRatio": 1.4586911891600107, - "candidate": [ - 0.11362100000042119, - 0.1842980000001262, - 0.11203899999964051, - 0.11047600000165403, - 0.11176900000100431, - 0.11115699999936623, - 0.1573370000005525 - ], - "baseline": [ - 0.36556000000018685, - 0.16793300000063027, - 0.09411200000067765, - 0.09358099999917613, - 0.12914400000045134, - 0.09942899999987276, - 0.5305889999999636 - ], - "selfA": [ - 0.08919400000013411, - 0.08680100000128732, - 0.0564650000014808, - 0.05620400000043446, - 0.04997499999990396, - 0.045178000000305474, - 0.04594900000120106 - ], - "selfB": [ - 0.09640500000023167, - 0.08885399999962829, - 0.046951000000262866, - 0.06075099999907252, - 0.04482800000005227, - 0.045087999998941086, - 0.04537800000071002 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.8888783261641711, - "noiseRatio": 1.787122069471407, - "lowerRatio": 0.6621500813350805, - "upperRatio": 1.1932410808307168, - "candidate": [ - 0.06457699999918987, - 0.0733909999999014, - 0.06271399999968708, - 0.06231300000035844, - 0.06481699999858392, - 0.05825700000059442, - 0.053641000000425265 - ], - "baseline": [ - 0.0720579999997426, - 0.05675599999995029, - 0.0934610000003886, - 0.0974869999990915, - 0.08617999999842141, - 0.04684099999940372, - 0.05688499999996566 - ], - "selfA": [ - 0.06630000000041036, - 0.17697700000098848, - 0.10446700000102283, - 0.06695099999888043, - 0.05578400000013062, - 0.04469699999935983, - 0.04814299999998184 - ], - "selfB": [ - 0.05961999999999534, - 0.07713599999988219, - 0.060921000000234926, - 0.0617130000009638, - 0.046731000000363565, - 0.04590900000039255, - 0.05319999999846914 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.08919400000013411, - 0.08680100000128732, - 0.0564650000014808, - 0.05620400000043446, - 0.04997499999990396, - 0.045178000000305474, - 0.04594900000120106 - ], - "baselineSelfB": [ - 0.09640500000023167, - 0.08885399999962829, - 0.046951000000262866, - 0.06075099999907252, - 0.04482800000005227, - 0.045087999998941086, - 0.04537800000071002 - ] - }, - "historicalReference": { - "A": 9, - "U": 7, - "C": 38, - "plateSwitches": 8, - "commands": [ - [ - 4, - -1 - ], - [ - 6, - -5 - ], - [ - 2, - -4 - ], - [ - 0, - 5 - ], - [ - 3, - 6 - ], - [ - 5, - 6 - ], - [ - 3, - 6 - ], - [ - 4, - -3 - ], - [ - 1, - -2 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "arto-room-door", - "title": "Door to Arto's room", - "optimumA": 7, - "solvers": { - "originalBfs": { - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - [ - 1, - -6 - ], - [ - 0, - -6 - ], - [ - 1, - -1 - ], - [ - 3, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - 5 - ], - [ - 2, - -3 - ] - ], - "samplesMs": [ - 3.1851729999998497, - 2.398339000001215, - 2.335194000001138, - 2.2831260000002658, - 3.200886000000537, - 2.3816029999998136, - 2.4000410000007832 - ], - "timingMs": { - "min": 2.2831260000002658, - "median": 2.398339000001215, - "p95": 3.200886000000537, - "max": 3.200886000000537 - } - }, - "upstream": { - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - [ - 1, - -6 - ], - [ - 0, - -5 - ], - [ - 3, - -5 - ], - [ - 0, - -6 - ], - [ - 4, - 5 - ], - [ - 2, - -3 - ], - [ - 1, - -1 - ] - ], - "samplesMs": [ - 4.736886999999115, - 4.581744999999501, - 4.6874729999999545, - 5.04921800000011, - 4.988276000000042, - 4.814645000000382, - 5.955120999999053 - ], - "timingMs": { - "min": 4.581744999999501, - "median": 4.814645000000382, - "p95": 5.955120999999053, - "max": 5.955120999999053 - } - }, - "candidate": { - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - [ - 1, - -6 - ], - [ - 0, - -6 - ], - [ - 1, - -1 - ], - [ - 3, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - 5 - ], - [ - 2, - -3 - ] - ], - "samplesMs": [ - 0.05984999999964202, - 0.04937400000017078, - 0.04800299999988056, - 0.05627499999900465, - 0.05228900000111025, - 0.0500850000007631, - 0.046920000000682194 - ], - "timingMs": { - "min": 0.046920000000682194, - "median": 0.0500850000007631, - "p95": 0.05984999999964202, - "max": 0.05984999999964202 - } - }, - "baseline": { - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - [ - 1, - -6 - ], - [ - 0, - -6 - ], - [ - 1, - -1 - ], - [ - 3, - -5 - ], - [ - 0, - -5 - ], - [ - 4, - 5 - ], - [ - 2, - -3 - ] - ], - "samplesMs": [ - 0.08412599999974191, - 0.04314499999964028, - 0.04024100000060571, - 0.052068000000872416, - 0.04796200000055251, - 0.0437660000006872, - 0.04570799999964947 - ], - "timingMs": { - "min": 0.04024100000060571, - "median": 0.04570799999964947, - "p95": 0.08412599999974191, - "max": 0.08412599999974191 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.0431734359770797, - "noiseRatio": 1.0796174793752589, - "lowerRatio": 0.8676100205792739, - "upperRatio": 1.2542626199748876 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0431734359770797, - "noiseRatio": 1.0796174793752589, - "lowerRatio": 0.8676100205792739, - "upperRatio": 1.2542626199748876, - "candidate": [ - 0.05984999999964202, - 0.04937400000017078, - 0.04800299999988056, - 0.05627499999900465, - 0.05228900000111025, - 0.0500850000007631, - 0.046920000000682194 - ], - "baseline": [ - 0.08412599999974191, - 0.04314499999964028, - 0.04024100000060571, - 0.052068000000872416, - 0.04796200000055251, - 0.0437660000006872, - 0.04570799999964947 - ], - "selfA": [ - 0.023726000001261127, - 0.022854999999253778, - 0.02294400000027963, - 0.022192999998878804, - 0.021392000000560074, - 0.023105000000214204, - 0.022123000000647153 - ], - "selfB": [ - 0.02638899999874411, - 0.02363599999989674, - 0.024046000000453205, - 0.02313500000127533, - 0.022304000000076485, - 0.022954999998546555, - 0.02207299999827228 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.1191980407051978, - "noiseRatio": 1.5737848701889292, - "lowerRatio": 1.0124621924834014, - "upperRatio": 1.2371862017345299, - "candidate": [ - 0.028372000000672415, - 0.032369000000471715, - 0.02709099999992759, - 0.025929000001269742, - 0.027221000000281492, - 0.027211000000534113, - 0.024817999999868334 - ], - "baseline": [ - 0.026841000000786153, - 0.02404699999897275, - 0.025529000000460655, - 0.025057999999262393, - 0.023436000001311186, - 0.023344999999608262, - 0.02386599999954342 - ], - "selfA": [ - 0.02581800000007206, - 0.023265000001629232, - 0.025829000000157976, - 0.026230000001305598, - 0.024676999999428517, - 0.02322499999900174, - 0.02371600000151375 - ], - "selfB": [ - 0.061432000000422704, - 0.022504000000481028, - 0.02480699999978242, - 0.024687000000994885, - 0.022753999999622465, - 0.02456700000038836, - 0.022234000000025844 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.023726000001261127, - 0.022854999999253778, - 0.02294400000027963, - 0.022192999998878804, - 0.021392000000560074, - 0.023105000000214204, - 0.022123000000647153 - ], - "baselineSelfB": [ - 0.02638899999874411, - 0.02363599999989674, - 0.024046000000453205, - 0.02313500000127533, - 0.022304000000076485, - 0.022954999998546555, - 0.02207299999827228 - ] - }, - "historicalReference": { - "A": 7, - "U": 5, - "C": 31, - "plateSwitches": 6, - "commands": [ - [ - 1, - -6 - ], - [ - 0, - -5 - ], - [ - 3, - -5 - ], - [ - 0, - -6 - ], - [ - 4, - 5 - ], - [ - 2, - -3 - ], - [ - 1, - -1 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "arto-far-bed-chest", - "title": "Arto's far bed chest", - "optimumA": 11, - "solvers": { - "originalBfs": { - "A": 11, - "U": 5, - "C": 44, - "plateSwitches": 10, - "commands": [ - [ - 1, - 4 - ], - [ - 2, - -2 - ], - [ - 5, - -2 - ], - [ - 3, - 3 - ], - [ - 4, - 6 - ], - [ - 1, - 6 - ], - [ - 5, - -6 - ], - [ - 4, - 6 - ], - [ - 1, - 3 - ], - [ - 2, - 3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 84.36533000000054, - 86.1215229999998, - 86.2261209999997, - 84.34919599999921, - 89.98912399999972, - 88.35752800000046, - 87.5321159999985 - ], - "timingMs": { - "min": 84.34919599999921, - "median": 86.2261209999997, - "p95": 89.98912399999972, - "max": 89.98912399999972 - } - }, - "upstream": { - "A": 13, - "U": 5, - "C": 40, - "plateSwitches": 12, - "commands": [ - [ - 3, - 1 - ], - [ - 4, - 2 - ], - [ - 5, - -2 - ], - [ - 3, - 2 - ], - [ - 1, - 4 - ], - [ - 4, - 4 - ], - [ - 5, - -3 - ], - [ - 3, - 3 - ], - [ - 2, - 1 - ], - [ - 1, - 6 - ], - [ - 4, - 6 - ], - [ - 5, - -3 - ], - [ - 1, - 3 - ] - ], - "samplesMs": [ - 32.24119299999984, - 27.58856100000048, - 27.363272000000507, - 27.101126999999906, - 27.185574999999517, - 28.424409000002925, - 31.55996800000139 - ], - "timingMs": { - "min": 27.101126999999906, - "median": 27.58856100000048, - "p95": 32.24119299999984, - "max": 32.24119299999984 - } - }, - "candidate": { - "A": 11, - "U": 5, - "C": 44, - "plateSwitches": 10, - "commands": [ - [ - 1, - 4 - ], - [ - 2, - -2 - ], - [ - 5, - -2 - ], - [ - 3, - 3 - ], - [ - 4, - 6 - ], - [ - 1, - 6 - ], - [ - 5, - -6 - ], - [ - 4, - 6 - ], - [ - 1, - 3 - ], - [ - 2, - 3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.16762200000084704, - 0.17408199999954377, - 0.16566899999997986, - 0.16762199999902805, - 0.49568600000020524, - 0.164677999997366, - 0.16835300000093412 - ], - "timingMs": { - "min": 0.164677999997366, - "median": 0.16762200000084704, - "p95": 0.49568600000020524, - "max": 0.49568600000020524 - } - }, - "baseline": { - "A": 11, - "U": 5, - "C": 44, - "plateSwitches": 10, - "commands": [ - [ - 1, - 4 - ], - [ - 2, - -2 - ], - [ - 5, - -2 - ], - [ - 3, - 3 - ], - [ - 4, - 6 - ], - [ - 1, - 6 - ], - [ - 5, - -6 - ], - [ - 4, - 6 - ], - [ - 1, - 3 - ], - [ - 2, - 3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.15266999999948894, - 0.15373100000033446, - 0.19375199999922188, - 0.1529500000015105, - 0.1573370000005525, - 0.1555740000003425, - 0.17360099999859813 - ], - "timingMs": { - "min": 0.15266999999948894, - "median": 0.1555740000003425, - "p95": 0.19375199999922188, - "max": 0.19375199999922188 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.2086442024743995, - "noiseRatio": 1.170322194976676, - "lowerRatio": 0.7670604680257382, - "upperRatio": 1.904440222209392 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.2086442024743995, - "noiseRatio": 1.170322194976676, - "lowerRatio": 0.7670604680257382, - "upperRatio": 1.904440222209392, - "candidate": [ - 0.16762200000084704, - 0.17408199999954377, - 0.16566899999997986, - 0.16762199999902805, - 0.49568600000020524, - 0.164677999997366, - 0.16835300000093412 - ], - "baseline": [ - 0.15266999999948894, - 0.15373100000033446, - 0.19375199999922188, - 0.1529500000015105, - 0.1573370000005525, - 0.1555740000003425, - 0.17360099999859813 - ], - "selfA": [ - 0.10941400000046997, - 0.09374099999877217, - 0.09103700000014214, - 0.10444699999970908, - 0.09672599999976228, - 0.08792199999879813, - 0.08673099999941769 - ], - "selfB": [ - 0.09156799999982468, - 0.1093540000001667, - 0.09035599999879196, - 0.08916500000123051, - 0.0903359999992972, - 0.10735199999908218, - 0.08832299999994575 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.116743880627492, - "noiseRatio": 4.782839047414779, - "lowerRatio": 0.7491885357259764, - "upperRatio": 1.6646235699675684, - "candidate": [ - 0.12749199999962002, - 0.13141800000084913, - 0.2237359999999171, - 0.11998999999923399, - 0.11928900000202702, - 0.1806719999985944, - 0.38905999999769847 - ], - "baseline": [ - 0.12363600000026054, - 0.09112699999968754, - 0.343576999999641, - 0.12779199999931734, - 0.14758200000142097, - 0.10246399999959976, - 0.23276999999870895 - ], - "selfA": [ - 0.11450200000035693, - 0.09765700000207289, - 0.1141910000005737, - 0.10691099999894504, - 0.11067600000023958, - 0.09602499999891734, - 0.28599000000031083 - ], - "selfB": [ - 0.09966999999960535, - 0.17578499999945052, - 1.8510360000000219, - 0.12241400000129943, - 0.10491800000090734, - 0.09441199999855598, - 0.11467199999970035 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.10941400000046997, - 0.09374099999877217, - 0.09103700000014214, - 0.10444699999970908, - 0.09672599999976228, - 0.08792199999879813, - 0.08673099999941769 - ], - "baselineSelfB": [ - 0.09156799999982468, - 0.1093540000001667, - 0.09035599999879196, - 0.08916500000123051, - 0.0903359999992972, - 0.10735199999908218, - 0.08832299999994575 - ] - }, - "historicalReference": { - "A": 13, - "U": 5, - "C": 40, - "plateSwitches": 12, - "commands": [ - [ - 3, - 1 - ], - [ - 4, - 2 - ], - [ - 5, - -2 - ], - [ - 3, - 2 - ], - [ - 1, - 4 - ], - [ - 4, - 4 - ], - [ - 5, - -3 - ], - [ - 3, - 3 - ], - [ - 2, - 1 - ], - [ - 1, - 6 - ], - [ - 4, - 6 - ], - [ - 5, - -3 - ], - [ - 1, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "regression", - "deltas": { - "A": -2, - "U": 0, - "C": 4, - "plateSwitches": -2 - }, - "regressions": [ - "C" - ] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "arto-near-bed-chest", - "title": "Arto's near bed chest", - "optimumA": 13, - "solvers": { - "originalBfs": { - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - [ - 4, - -1 - ], - [ - 0, - 6 - ], - [ - 3, - 2 - ], - [ - 2, - 4 - ], - [ - 5, - -3 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - 6 - ], - [ - 3, - 6 - ], - [ - 2, - 6 - ], - [ - 5, - -6 - ], - [ - 1, - -3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 93.48326200000156, - 99.21483900000021, - 91.84003999999914, - 114.51992299999984, - 114.96519800000169, - 105.04207799999858, - 116.20399300000281 - ], - "timingMs": { - "min": 91.84003999999914, - "median": 105.04207799999858, - "p95": 116.20399300000281, - "max": 116.20399300000281 - } - }, - "upstream": { - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - [ - 4, - -1 - ], - [ - 5, - -3 - ], - [ - 3, - 4 - ], - [ - 0, - 6 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 2, - 4 - ], - [ - 3, - 4 - ], - [ - 5, - -6 - ], - [ - 2, - 6 - ], - [ - 3, - 3 - ], - [ - 0, - 6 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 72.72948800000086, - 70.43284199999835, - 84.94343099999969, - 75.99135699999897, - 76.52235699999801, - 102.69168100000024, - 76.19374199999947 - ], - "timingMs": { - "min": 70.43284199999835, - "median": 76.19374199999947, - "p95": 102.69168100000024, - "max": 102.69168100000024 - } - }, - "candidate": { - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - [ - 4, - -1 - ], - [ - 0, - 6 - ], - [ - 5, - -3 - ], - [ - 3, - 4 - ], - [ - 2, - 6 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 3, - 4 - ], - [ - 0, - 6 - ], - [ - 2, - 4 - ], - [ - 5, - -6 - ], - [ - 1, - -3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.44626200000129757, - 0.44416800000180956, - 0.4871129999992263, - 0.44914600000265636, - 0.4682539999994333, - 0.46663300000000163, - 0.44776400000046124 - ], - "timingMs": { - "min": 0.44416800000180956, - "median": 0.44914600000265636, - "p95": 0.4871129999992263, - "max": 0.4871129999992263 - } - }, - "baseline": { - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - [ - 4, - -1 - ], - [ - 0, - 6 - ], - [ - 5, - -3 - ], - [ - 3, - 4 - ], - [ - 2, - 6 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 3, - 4 - ], - [ - 0, - 6 - ], - [ - 2, - 4 - ], - [ - 5, - -6 - ], - [ - 1, - -3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.4818750000013097, - 0.47723799999948824, - 0.44000299999970593, - 0.4739029999982449, - 0.4392410000000382, - 0.750529999997525, - 0.5789120000008552 - ], - "timingMs": { - "min": 0.4392410000000382, - "median": 0.47723799999948824, - "p95": 0.750529999997525, - "max": 0.750529999997525 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.8960019137269032, - "noiseRatio": 1.536030275362091, - "lowerRatio": 0.7277393996588619, - "upperRatio": 1.1031688400801245 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.8960019137269032, - "noiseRatio": 1.536030275362091, - "lowerRatio": 0.7277393996588619, - "upperRatio": 1.1031688400801245, - "candidate": [ - 0.44626200000129757, - 0.44416800000180956, - 0.4871129999992263, - 0.44914600000265636, - 0.4682539999994333, - 0.46663300000000163, - 0.44776400000046124 - ], - "baseline": [ - 0.4818750000013097, - 0.47723799999948824, - 0.44000299999970593, - 0.4739029999982449, - 0.4392410000000382, - 0.750529999997525, - 0.5789120000008552 - ], - "selfA": [ - 0.48795500000051106, - 0.7330430000001797, - 0.39147899999807123, - 0.5262120000006689, - 0.383257000001322, - 0.3927210000001651, - 0.36360699999931967 - ], - "selfB": [ - 0.44363700000030803, - 0.6561079999992216, - 0.5254009999989648, - 0.9030800000000454, - 0.5744040000026871, - 0.393011000000115, - 0.40115399999922374 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.011465653312673, - "noiseRatio": 1.4316363830277896, - "lowerRatio": 0.5961884217670623, - "upperRatio": 1.7160057634110757, - "candidate": [ - 0.42741300000125193, - 0.5093459999989136, - 0.3987199999974109, - 1.0375119999989693, - 0.4779290000005858, - 0.47367300000041723, - 0.42971699999907287 - ], - "baseline": [ - 0.44182500000169966, - 0.41073900000264985, - 0.9937959999988379, - 0.4736629999970319, - 0.4423960000021907, - 0.5372089999982563, - 0.3984290000007604 - ], - "selfA": [ - 0.4276540000028035, - 0.6434589999989839, - 0.4788410000001022, - 0.7158769999987271, - 0.39983199999915087, - 0.42971599999873433, - 0.38802399999985937 - ], - "selfB": [ - 0.37017700000069453, - 0.5140929999979562, - 0.45360299999811104, - 0.4186599999993632, - 0.3655299999991257, - 0.3625659999997879, - 0.382846000000427 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.48795500000051106, - 0.7330430000001797, - 0.39147899999807123, - 0.5262120000006689, - 0.383257000001322, - 0.3927210000001651, - 0.36360699999931967 - ], - "baselineSelfB": [ - 0.44363700000030803, - 0.6561079999992216, - 0.5254009999989648, - 0.9030800000000454, - 0.5744040000026871, - 0.393011000000115, - 0.40115399999922374 - ] - }, - "historicalReference": { - "A": 13, - "U": 6, - "C": 52, - "plateSwitches": 12, - "commands": [ - [ - 4, - -1 - ], - [ - 5, - -3 - ], - [ - 3, - 4 - ], - [ - 0, - 6 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 2, - 4 - ], - [ - 3, - 4 - ], - [ - 5, - -6 - ], - [ - 2, - 6 - ], - [ - 3, - 3 - ], - [ - 0, - 6 - ], - [ - 1, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "gomez-bedrooms-door", - "title": "Door on Gomez's bedrooms floor", - "optimumA": 14, - "solvers": { - "originalBfs": { - "A": 14, - "U": 6, - "C": 45, - "plateSwitches": 13, - "commands": [ - [ - 0, - 1 - ], - [ - 2, - 1 - ], - [ - 3, - -6 - ], - [ - 1, - 3 - ], - [ - 0, - 4 - ], - [ - 2, - 4 - ], - [ - 3, - -3 - ], - [ - 4, - 5 - ], - [ - 0, - 4 - ], - [ - 1, - 3 - ], - [ - 2, - 3 - ], - [ - 4, - 4 - ], - [ - 0, - 3 - ], - [ - 5, - 1 - ] - ], - "samplesMs": [ - 106.72043499999927, - 101.2885090000018, - 104.10252500000206, - 110.61321400000088, - 102.18899500000043, - 101.30750800000169, - 104.96197799999936 - ], - "timingMs": { - "min": 101.2885090000018, - "median": 104.10252500000206, - "p95": 110.61321400000088, - "max": 110.61321400000088 - } - }, - "upstream": { - "A": 14, - "U": 6, - "C": 45, - "plateSwitches": 13, - "commands": [ - [ - 3, - -3 - ], - [ - 0, - 1 - ], - [ - 2, - 1 - ], - [ - 1, - 3 - ], - [ - 0, - 4 - ], - [ - 4, - 4 - ], - [ - 2, - 4 - ], - [ - 0, - 4 - ], - [ - 5, - 1 - ], - [ - 4, - 5 - ], - [ - 3, - -6 - ], - [ - 2, - 3 - ], - [ - 1, - 3 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 37.79504199999792, - 41.81860699999743, - 36.81990399999995, - 38.27305100000012, - 38.40403800000058, - 38.76095499999792, - 40.954696000000695 - ], - "timingMs": { - "min": 36.81990399999995, - "median": 38.40403800000058, - "p95": 41.81860699999743, - "max": 41.81860699999743 - } - }, - "candidate": { - "A": 14, - "U": 6, - "C": 49, - "plateSwitches": 13, - "commands": [ - [ - 3, - -6 - ], - [ - 5, - -2 - ], - [ - 2, - 2 - ], - [ - 0, - 3 - ], - [ - 1, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - 3 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 5, - 3 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 5.996425000001182, - 7.218203000000358, - 5.7388670000000275, - 6.5628969999997935, - 5.623243999998522, - 5.476120999999694, - 5.6087320000006 - ], - "timingMs": { - "min": 5.476120999999694, - "median": 5.7388670000000275, - "p95": 7.218203000000358, - "max": 7.218203000000358 - } - }, - "baseline": { - "A": 14, - "U": 6, - "C": 49, - "plateSwitches": 13, - "commands": [ - [ - 3, - -6 - ], - [ - 5, - -2 - ], - [ - 2, - 2 - ], - [ - 0, - 3 - ], - [ - 1, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - 3 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 5, - 3 - ], - [ - 0, - 3 - ], - [ - 2, - 3 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 6.136445000000094, - 5.7011100000017905, - 8.624781000002258, - 6.272659999998723, - 5.65148599999884, - 5.458033999999316, - 5.763394000001426 - ], - "timingMs": { - "min": 5.458033999999316, - "median": 5.763394000001426, - "p95": 8.624781000002258, - "max": 8.624781000002258 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.9748660442098722, - "noiseRatio": 1.0587583552979651, - "lowerRatio": 0.7972629533021802, - "upperRatio": 1.1920330679070141 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9748660442098722, - "noiseRatio": 1.0587583552979651, - "lowerRatio": 0.7972629533021802, - "upperRatio": 1.1920330679070141, - "candidate": [ - 5.996425000001182, - 7.218203000000358, - 5.7388670000000275, - 6.5628969999997935, - 5.623243999998522, - 5.476120999999694, - 5.6087320000006 - ], - "baseline": [ - 6.136445000000094, - 5.7011100000017905, - 8.624781000002258, - 6.272659999998723, - 5.65148599999884, - 5.458033999999316, - 5.763394000001426 - ], - "selfA": [ - 5.50688799999989, - 5.357202999999572, - 5.406025999996928, - 5.5422100000032515, - 6.047120999999606, - 5.859859999996843, - 5.612536999997246 - ], - "selfB": [ - 5.416801999999734, - 5.783855000001495, - 5.405395999998291, - 5.651635999998689, - 5.6948410000004515, - 5.588550000000396, - 5.437733999999182 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0102552348988454, - "noiseRatio": 1.1308450438053403, - "lowerRatio": 0.8361241871224652, - "upperRatio": 1.2206507781493396, - "candidate": [ - 6.0461189999987255, - 7.764476000000286, - 5.547810000000027, - 5.909584000000905, - 5.314168000000791, - 5.59868600000118, - 5.312254999997094 - ], - "baseline": [ - 5.583944000001793, - 5.4918350000007194, - 5.776493999997911, - 5.521318999999494, - 6.0751430000018445, - 6.903519999999844, - 5.521500000002561 - ], - "selfA": [ - 5.559316999999282, - 5.7145500000005995, - 5.912598999999318, - 5.657123000000865, - 5.510543000000325, - 5.955432999999175, - 5.640048000001116 - ], - "selfB": [ - 5.486466999998811, - 5.563733999999386, - 6.504758999999467, - 6.636227999999392, - 5.643724000001384, - 6.5993019999987155, - 5.656123000000662 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 5.50688799999989, - 5.357202999999572, - 5.406025999996928, - 5.5422100000032515, - 6.047120999999606, - 5.859859999996843, - 5.612536999997246 - ], - "baselineSelfB": [ - 5.416801999999734, - 5.783855000001495, - 5.405395999998291, - 5.651635999998689, - 5.6948410000004515, - 5.588550000000396, - 5.437733999999182 - ] - }, - "historicalReference": { - "A": 14, - "U": 6, - "C": 45, - "plateSwitches": 13, - "commands": [ - [ - 3, - -3 - ], - [ - 0, - 1 - ], - [ - 2, - 1 - ], - [ - 1, - 3 - ], - [ - 0, - 4 - ], - [ - 4, - 4 - ], - [ - 2, - 4 - ], - [ - 0, - 4 - ], - [ - 5, - 1 - ], - [ - 4, - 5 - ], - [ - 3, - -6 - ], - [ - 2, - 3 - ], - [ - 1, - 3 - ], - [ - 0, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "regression", - "deltas": { - "A": 0, - "U": 0, - "C": 4, - "plateSwitches": 0 - }, - "regressions": [ - "C" - ] - }, - "upstream": { - "quality": "regression", - "deltas": { - "A": 0, - "U": 0, - "C": 4, - "plateSwitches": 0 - }, - "regressions": [ - "C" - ] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "gomez-bedrooms-far-chest", - "title": "Far bed chest (bedrooms floor)", - "optimumA": 14, - "solvers": { - "originalBfs": { - "A": 14, - "U": 5, - "C": 43, - "plateSwitches": 13, - "commands": [ - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - -3 - ], - [ - 2, - -2 - ], - [ - 1, - -2 - ], - [ - 3, - 4 - ], - [ - 2, - -5 - ], - [ - 0, - -4 - ], - [ - 1, - -2 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 11.207657999999356, - 15.987766999998712, - 11.984307000002445, - 13.01028999999835, - 10.965552999998181, - 10.679371999998693, - 10.33407299999817 - ], - "timingMs": { - "min": 10.33407299999817, - "median": 11.207657999999356, - "p95": 15.987766999998712, - "max": 15.987766999998712 - } - }, - "upstream": { - "A": 16, - "U": 5, - "C": 39, - "plateSwitches": 15, - "commands": [ - [ - 0, - 1 - ], - [ - 3, - 1 - ], - [ - 2, - -1 - ], - [ - 3, - 1 - ], - [ - 2, - -1 - ], - [ - 4, - -2 - ], - [ - 2, - -2 - ], - [ - 1, - -2 - ], - [ - 3, - 4 - ], - [ - 2, - -4 - ], - [ - 3, - 4 - ], - [ - 2, - -2 - ], - [ - 4, - -4 - ], - [ - 2, - -3 - ], - [ - 0, - -5 - ], - [ - 1, - -2 - ] - ], - "samplesMs": [ - 6.223947000002227, - 5.5543890000008105, - 6.192258999999467, - 5.621289999999135, - 6.539220999999088, - 5.584665000002133, - 5.526977999998053 - ], - "timingMs": { - "min": 5.526977999998053, - "median": 5.621289999999135, - "p95": 6.539220999999088, - "max": 6.539220999999088 - } - }, - "candidate": { - "A": 14, - "U": 5, - "C": 43, - "plateSwitches": 13, - "commands": [ - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 3, - 2 - ], - [ - 4, - -3 - ], - [ - 2, - -4 - ], - [ - 0, - -5 - ], - [ - 1, - -4 - ], - [ - 0, - -2 - ], - [ - 3, - 2 - ], - [ - 2, - -3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 1.5610300000007555, - 1.3212390000007872, - 2.1247980000007374, - 1.5893009999999776, - 1.3724660000007134, - 1.3105820000018866, - 1.3346980000023905 - ], - "timingMs": { - "min": 1.3105820000018866, - "median": 1.3724660000007134, - "p95": 2.1247980000007374, - "max": 2.1247980000007374 - } - }, - "baseline": { - "A": 14, - "U": 5, - "C": 43, - "plateSwitches": 13, - "commands": [ - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 3, - 2 - ], - [ - 4, - -3 - ], - [ - 2, - -4 - ], - [ - 0, - -5 - ], - [ - 1, - -4 - ], - [ - 0, - -2 - ], - [ - 3, - 2 - ], - [ - 2, - -3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 1.5528970000013942, - 1.537133999998332, - 2.3746179999980086, - 1.6453760000003967, - 1.4976739999983693, - 1.6183649999984482, - 1.537694000002375 - ], - "timingMs": { - "min": 1.4976739999983693, - "median": 1.5528970000013942, - "p95": 2.3746179999980086, - "max": 2.3746179999980086 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.9007376641859475, - "noiseRatio": 1.1068051486703363, - "lowerRatio": 0.8341419560467856, - "upperRatio": 0.9726501991678389 - }, - "timingAttempts": [ - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.9007376641859475, - "noiseRatio": 1.1068051486703363, - "lowerRatio": 0.8341419560467856, - "upperRatio": 0.9726501991678389, - "candidate": [ - 1.5610300000007555, - 1.3212390000007872, - 2.1247980000007374, - 1.5893009999999776, - 1.3724660000007134, - 1.3105820000018866, - 1.3346980000023905 - ], - "baseline": [ - 1.5528970000013942, - 1.537133999998332, - 2.3746179999980086, - 1.6453760000003967, - 1.4976739999983693, - 1.6183649999984482, - 1.537694000002375 - ], - "selfA": [ - 1.3812289999987115, - 1.5048850000021048, - 1.3781239999989339, - 1.5837439999995695, - 1.4039530000009108, - 1.347647999999026, - 1.3802670000004582 - ], - "selfB": [ - 1.322570000000269, - 1.354457999997976, - 1.4024810000009893, - 1.4063370000003488, - 1.3295709999983956, - 1.3724760000004608, - 1.3401770000018587 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 1.3812289999987115, - 1.5048850000021048, - 1.3781239999989339, - 1.5837439999995695, - 1.4039530000009108, - 1.347647999999026, - 1.3802670000004582 - ], - "baselineSelfB": [ - 1.322570000000269, - 1.354457999997976, - 1.4024810000009893, - 1.4063370000003488, - 1.3295709999983956, - 1.3724760000004608, - 1.3401770000018587 - ] - }, - "historicalReference": { - "A": 16, - "U": 5, - "C": 39, - "plateSwitches": 15, - "commands": [ - [ - 0, - 1 - ], - [ - 3, - 1 - ], - [ - 2, - -1 - ], - [ - 3, - 1 - ], - [ - 2, - -1 - ], - [ - 4, - -2 - ], - [ - 2, - -2 - ], - [ - 1, - -2 - ], - [ - 3, - 4 - ], - [ - 2, - -4 - ], - [ - 3, - 4 - ], - [ - 2, - -2 - ], - [ - 4, - -4 - ], - [ - 2, - -3 - ], - [ - 0, - -5 - ], - [ - 1, - -2 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "regression", - "deltas": { - "A": -2, - "U": 0, - "C": 4, - "plateSwitches": -2 - }, - "regressions": [ - "C" - ] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "gomez-bedrooms-near-chest", - "title": "Near bed chest (bedrooms floor)", - "optimumA": 12, - "solvers": { - "originalBfs": { - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - [ - 0, - 4 - ], - [ - 2, - 1 - ], - [ - 5, - -6 - ], - [ - 4, - -6 - ], - [ - 0, - 6 - ], - [ - 5, - -6 - ], - [ - 1, - -6 - ], - [ - 0, - 6 - ], - [ - 3, - -3 - ], - [ - 0, - 3 - ], - [ - 5, - -6 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 75.92996499999936, - 72.20332599999892, - 83.12275500000032, - 89.42843999999968, - 79.52953200000047, - 76.69865199999913, - 78.9937460000001 - ], - "timingMs": { - "min": 72.20332599999892, - "median": 78.9937460000001, - "p95": 89.42843999999968, - "max": 89.42843999999968 - } - }, - "upstream": { - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - [ - 5, - -6 - ], - [ - 0, - 4 - ], - [ - 4, - -6 - ], - [ - 5, - -6 - ], - [ - 2, - 1 - ], - [ - 0, - 6 - ], - [ - 1, - -6 - ], - [ - 5, - -6 - ], - [ - 0, - 6 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 43.113225000000966, - 44.942173999999795, - 44.68240700000024, - 42.28151400000206, - 43.22101299999849, - 43.647359000002325, - 49.91504200000054 - ], - "timingMs": { - "min": 42.28151400000206, - "median": 43.647359000002325, - "p95": 49.91504200000054, - "max": 49.91504200000054 - } - }, - "candidate": { - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - [ - 0, - 4 - ], - [ - 2, - 1 - ], - [ - 5, - -6 - ], - [ - 4, - -6 - ], - [ - 0, - 6 - ], - [ - 5, - -6 - ], - [ - 1, - -6 - ], - [ - 0, - 6 - ], - [ - 3, - -3 - ], - [ - 0, - 3 - ], - [ - 5, - -6 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 0.10293500000261702, - 0.09918000000106986, - 0.10355600000184495, - 0.10368599999856087, - 0.09805799999958253, - 0.0970660000020871, - 0.09674600000289502 - ], - "timingMs": { - "min": 0.09674600000289502, - "median": 0.09918000000106986, - "p95": 0.10368599999856087, - "max": 0.10368599999856087 - } - }, - "baseline": { - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - [ - 0, - 4 - ], - [ - 2, - 1 - ], - [ - 5, - -6 - ], - [ - 4, - -6 - ], - [ - 0, - 6 - ], - [ - 5, - -6 - ], - [ - 1, - -6 - ], - [ - 0, - 6 - ], - [ - 3, - -3 - ], - [ - 0, - 3 - ], - [ - 5, - -6 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 0.08027999999831081, - 0.08136200000080862, - 0.11122799999793642, - 0.4359660000009171, - 0.15506299999833573, - 0.10341499999776715, - 0.11153799999738112 - ], - "timingMs": { - "min": 0.08027999999831081, - "median": 0.11122799999793642, - "p95": 0.4359660000009171, - "max": 0.4359660000009171 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.7815906384700172, - "noiseRatio": 1.2514474655499743, - "lowerRatio": 0.427905955251845, - "upperRatio": 1.4276125832005118 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.7815906384700172, - "noiseRatio": 1.2514474655499743, - "lowerRatio": 0.427905955251845, - "upperRatio": 1.4276125832005118, - "candidate": [ - 0.10293500000261702, - 0.09918000000106986, - 0.10355600000184495, - 0.10368599999856087, - 0.09805799999958253, - 0.0970660000020871, - 0.09674600000289502 - ], - "baseline": [ - 0.08027999999831081, - 0.08136200000080862, - 0.11122799999793642, - 0.4359660000009171, - 0.15506299999833573, - 0.10341499999776715, - 0.11153799999738112 - ], - "selfA": [ - 0.04046100000050501, - 0.03728599999885773, - 0.0637959999985469, - 0.04100200000175391, - 0.03611399999863352, - 0.04031100000065635, - 0.03777700000136974 - ], - "selfB": [ - 0.04563899999993737, - 0.038969000001088716, - 0.04062100000010105, - 0.040140000000974396, - 0.03786699999909615, - 0.04299399999945308, - 0.03994999999849824 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9995119994496934, - "noiseRatio": 1.2666966883771833, - "lowerRatio": 0.8279734422127132, - "upperRatio": 1.206589711831924, - "candidate": [ - 0.046839999999065185, - 0.043164999999135034, - 0.04465699999855133, - 0.04624999999941792, - 0.044226999998500105, - 0.04964499999914551, - 0.0484230000001844 - ], - "baseline": [ - 0.04392600000210223, - 0.04267500000059954, - 0.04642999999850872, - 0.045588999997562496, - 0.06316499999957159, - 0.03918899999916903, - 0.04535799999939627 - ], - "selfA": [ - 0.042223999997077044, - 0.04125199999907636, - 0.040660999999090564, - 0.040460000000166474, - 0.04225299999961862, - 0.04138199999943026, - 0.06836300000213669 - ], - "selfB": [ - 0.04063099999984843, - 0.038908999998966465, - 0.04013999999733642, - 0.04837300000144751, - 0.03929900000002817, - 0.04409599999780767, - 0.045709000001807 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.04046100000050501, - 0.03728599999885773, - 0.0637959999985469, - 0.04100200000175391, - 0.03611399999863352, - 0.04031100000065635, - 0.03777700000136974 - ], - "baselineSelfB": [ - 0.04563899999993737, - 0.038969000001088716, - 0.04062100000010105, - 0.040140000000974396, - 0.03786699999909615, - 0.04299399999945308, - 0.03994999999849824 - ] - }, - "historicalReference": { - "A": 12, - "U": 6, - "C": 56, - "plateSwitches": 11, - "commands": [ - [ - 5, - -6 - ], - [ - 0, - 4 - ], - [ - 4, - -6 - ], - [ - 5, - -6 - ], - [ - 2, - 1 - ], - [ - 0, - 6 - ], - [ - 1, - -6 - ], - [ - 5, - -6 - ], - [ - 0, - 6 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 0, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "raven-tower-left-chest", - "title": "Raven Tower left chest", - "optimumA": 11, - "solvers": { - "originalBfs": { - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - [ - 1, - -5 - ], - [ - 2, - -1 - ], - [ - 3, - 2 - ], - [ - 0, - 2 - ], - [ - 3, - 2 - ], - [ - 4, - 4 - ], - [ - 5, - -4 - ], - [ - 0, - 6 - ], - [ - 2, - -3 - ], - [ - 3, - 3 - ], - [ - 4, - 3 - ] - ], - "samplesMs": [ - 128.53769000000102, - 118.29718199999843, - 116.36731299999883, - 108.86097200000222, - 109.44556999999986, - 108.36314700000003, - 106.78033499999947 - ], - "timingMs": { - "min": 106.78033499999947, - "median": 109.44556999999986, - "p95": 128.53769000000102, - "max": 128.53769000000102 - } - }, - "upstream": { - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - [ - 5, - -3 - ], - [ - 4, - 1 - ], - [ - 3, - 2 - ], - [ - 0, - 5 - ], - [ - 2, - -4 - ], - [ - 5, - -1 - ], - [ - 4, - 6 - ], - [ - 1, - -2 - ], - [ - 3, - 5 - ], - [ - 1, - -3 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 50.84226800000033, - 53.75917699999991, - 52.739472999997815, - 53.35408800000005, - 53.098704, - 50.198529000001145, - 50.16471799999999 - ], - "timingMs": { - "min": 50.16471799999999, - "median": 52.739472999997815, - "p95": 53.75917699999991, - "max": 53.75917699999991 - } - }, - "candidate": { - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - [ - 4, - 2 - ], - [ - 5, - -3 - ], - [ - 3, - 2 - ], - [ - 0, - 5 - ], - [ - 2, - -4 - ], - [ - 4, - 5 - ], - [ - 5, - -1 - ], - [ - 1, - -3 - ], - [ - 3, - 5 - ], - [ - 0, - 3 - ], - [ - 1, - -2 - ] - ], - "samplesMs": [ - 1.825286999999662, - 1.4545190000026196, - 1.3061560000023746, - 1.358064000000013, - 1.3477190000012342, - 1.7332279999973252, - 1.6534879999999248 - ], - "timingMs": { - "min": 1.3061560000023746, - "median": 1.4545190000026196, - "p95": 1.825286999999662, - "max": 1.825286999999662 - } - }, - "baseline": { - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - [ - 4, - 2 - ], - [ - 5, - -3 - ], - [ - 3, - 2 - ], - [ - 0, - 5 - ], - [ - 2, - -4 - ], - [ - 4, - 5 - ], - [ - 5, - -1 - ], - [ - 1, - -3 - ], - [ - 3, - 5 - ], - [ - 0, - 3 - ], - [ - 1, - -2 - ] - ], - "samplesMs": [ - 1.441689999999653, - 1.3271779999995488, - 1.8665989999972226, - 1.3084890000027372, - 1.3080389999995532, - 1.34014599999864, - 1.374098000000231 - ], - "timingMs": { - "min": 1.3080389999995532, - "median": 1.34014599999864, - "p95": 1.8665989999972226, - "max": 1.8665989999972226 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.0709587699984509, - "noiseRatio": 1.1937145569796526, - "lowerRatio": 0.860353591160936, - "upperRatio": 1.3331178004254396 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0709587699984509, - "noiseRatio": 1.1937145569796526, - "lowerRatio": 0.860353591160936, - "upperRatio": 1.3331178004254396, - "candidate": [ - 1.825286999999662, - 1.4545190000026196, - 1.3061560000023746, - 1.358064000000013, - 1.3477190000012342, - 1.7332279999973252, - 1.6534879999999248 - ], - "baseline": [ - 1.441689999999653, - 1.3271779999995488, - 1.8665989999972226, - 1.3084890000027372, - 1.3080389999995532, - 1.34014599999864, - 1.374098000000231 - ], - "selfA": [ - 1.503101999998762, - 1.866879000001063, - 1.5306730000011157, - 1.3268359999965469, - 1.3114040000000386, - 1.2954500000014377, - 1.229760999998689 - ], - "selfB": [ - 1.5353610000020126, - 1.428369000001112, - 1.3675080000029993, - 1.263241000000562, - 1.2479779999994207, - 1.217211999999563, - 1.315429000002041 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9491387621241855, - "noiseRatio": 1.2281664964021166, - "lowerRatio": 0.613392415583522, - "upperRatio": 1.468659159910929, - "candidate": [ - 2.7835589999995136, - 1.2678280000000086, - 1.3855049999983748, - 1.2308220000013534, - 1.2134860000005574, - 1.4232220000012603, - 1.6662770000002638 - ], - "baseline": [ - 1.4958709999991697, - 1.26497400000153, - 1.5284000000028755, - 1.3558599999996659, - 1.339745999997831, - 1.2866360000007262, - 3.6923679999999877 - ], - "selfA": [ - 1.6474100000014005, - 1.4734779999998864, - 1.4286810000012338, - 1.2728050000005169, - 1.4605379999993602, - 1.2567310000013094, - 1.393856999999116 - ], - "selfB": [ - 1.2881079999970098, - 1.5525569999990694, - 1.2827010000000882, - 1.2742380000017874, - 1.4243639999986044, - 1.3787149999989197, - 1.9565249999977823 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 1.503101999998762, - 1.866879000001063, - 1.5306730000011157, - 1.3268359999965469, - 1.3114040000000386, - 1.2954500000014377, - 1.229760999998689 - ], - "baselineSelfB": [ - 1.5353610000020126, - 1.428369000001112, - 1.3675080000029993, - 1.263241000000562, - 1.2479779999994207, - 1.217211999999563, - 1.315429000002041 - ] - }, - "historicalReference": { - "A": 11, - "U": 6, - "C": 35, - "plateSwitches": 10, - "commands": [ - [ - 5, - -3 - ], - [ - 4, - 1 - ], - [ - 3, - 2 - ], - [ - 0, - 5 - ], - [ - 2, - -4 - ], - [ - 5, - -1 - ], - [ - 4, - 6 - ], - [ - 1, - -2 - ], - [ - 3, - 5 - ], - [ - 1, - -3 - ], - [ - 0, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "raven-tower-right-chest", - "title": "Raven Tower right chest", - "optimumA": 8, - "solvers": { - "originalBfs": { - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - [ - 1, - 5 - ], - [ - 2, - -1 - ], - [ - 5, - -5 - ], - [ - 0, - -5 - ], - [ - 2, - -2 - ], - [ - 3, - 2 - ], - [ - 4, - 1 - ], - [ - 5, - -2 - ] - ], - "samplesMs": [ - 36.35005600000295, - 36.217366999997466, - 35.26854900000035, - 37.717085000000225, - 38.20482799999809, - 36.07308000000194, - 37.628781999999774 - ], - "timingMs": { - "min": 35.26854900000035, - "median": 36.35005600000295, - "p95": 38.20482799999809, - "max": 38.20482799999809 - } - }, - "upstream": { - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - [ - 5, - -6 - ], - [ - 0, - -2 - ], - [ - 5, - -1 - ], - [ - 4, - 1 - ], - [ - 3, - 2 - ], - [ - 1, - 5 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 16.226090000000113, - 16.07094599999982, - 16.15751700000328, - 16.43437400000039, - 16.67321300000185, - 16.036153999997623, - 16.684089000002132 - ], - "timingMs": { - "min": 16.036153999997623, - "median": 16.226090000000113, - "p95": 16.684089000002132, - "max": 16.684089000002132 - } - }, - "candidate": { - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - [ - 1, - 5 - ], - [ - 4, - 1 - ], - [ - 5, - -6 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -2 - ], - [ - 3, - 2 - ], - [ - 5, - -1 - ] - ], - "samplesMs": [ - 0.13393200000064098, - 0.15595500000199536, - 0.12147299999924144, - 0.13800799999808078, - 0.13398199999937788, - 0.13488299999880837, - 0.13035599999784608 - ], - "timingMs": { - "min": 0.12147299999924144, - "median": 0.13398199999937788, - "p95": 0.15595500000199536, - "max": 0.15595500000199536 - } - }, - "baseline": { - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - [ - 1, - 5 - ], - [ - 4, - 1 - ], - [ - 5, - -6 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -2 - ], - [ - 3, - 2 - ], - [ - 5, - -1 - ] - ], - "samplesMs": [ - 0.14509800000087125, - 0.12792199999967124, - 0.12039100000038161, - 0.12083200000051875, - 0.24475800000072923, - 0.12461700000130804, - 0.11881899999934831 - ], - "timingMs": { - "min": 0.11881899999934831, - "median": 0.12461700000130804, - "p95": 0.24475800000072923, - "max": 0.24475800000072923 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.9758943552814838, - "noiseRatio": 1.7764179621697334, - "lowerRatio": 0.7351910747245831, - "upperRatio": 1.2954044539061347 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9758943552814838, - "noiseRatio": 1.7764179621697334, - "lowerRatio": 0.7351910747245831, - "upperRatio": 1.2954044539061347, - "candidate": [ - 0.13393200000064098, - 0.15595500000199536, - 0.12147299999924144, - 0.13800799999808078, - 0.13398199999937788, - 0.13488299999880837, - 0.13035599999784608 - ], - "baseline": [ - 0.14509800000087125, - 0.12792199999967124, - 0.12039100000038161, - 0.12083200000051875, - 0.24475800000072923, - 0.12461700000130804, - 0.11881899999934831 - ], - "selfA": [ - 0.07413100000121631, - 0.07765700000163633, - 0.07877899999948568, - 0.07014500000150292, - 0.07724599999710335, - 0.06782300000122632, - 0.06463699999949313 - ], - "selfB": [ - 0.14591000000291388, - 0.08611000000018976, - 0.08727100000032806, - 0.07589399999778834, - 0.1568760000009206, - 0.07953999999881489, - 0.06701100000282167 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.8266910608114032, - "noiseRatio": 1.3734118218671454, - "lowerRatio": 0.29445294009737427, - "upperRatio": 2.320975670337949, - "candidate": [ - 0.0999100000008184, - 0.09514299999864306, - 0.08793199999854551, - 0.09991099999751896, - 0.11775699999998324, - 0.12473799999861512, - 0.08612999999968451 - ], - "baseline": [ - 0.09277900000233785, - 0.07643499999903725, - 0.9708470000005036, - 0.08841299999767216, - 0.09959000000162632, - 0.0801610000016808, - 0.08239400000093156 - ], - "selfA": [ - 0.08329599999706261, - 0.12485799999922165, - 0.08619900000121561, - 0.0703060000014375, - 0.11444300000221119, - 0.07681600000069011, - 0.07889799999975367 - ], - "selfB": [ - 0.07929900000090129, - 0.0971559999998135, - 0.07422199999928125, - 0.10997599999973318, - 0.08546899999782909, - 0.06767200000103912, - 0.07990000000063446 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.07413100000121631, - 0.07765700000163633, - 0.07877899999948568, - 0.07014500000150292, - 0.07724599999710335, - 0.06782300000122632, - 0.06463699999949313 - ], - "baselineSelfB": [ - 0.14591000000291388, - 0.08611000000018976, - 0.08727100000032806, - 0.07589399999778834, - 0.1568760000009206, - 0.07953999999881489, - 0.06701100000282167 - ] - }, - "historicalReference": { - "A": 8, - "U": 6, - "C": 23, - "plateSwitches": 7, - "commands": [ - [ - 5, - -6 - ], - [ - 0, - -2 - ], - [ - 5, - -1 - ], - [ - 4, - 1 - ], - [ - 3, - 2 - ], - [ - 1, - 5 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "raven-tower-floor2-stairs-chest", - "title": "Chest by stairs (Raven Tower floor 2)", - "optimumA": 16, - "solvers": { - "originalBfs": { - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - [ - 0, - -1 - ], - [ - 4, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - 4 - ], - [ - 3, - -2 - ], - [ - 4, - -3 - ], - [ - 1, - 3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - -3 - ], - [ - 2, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -6 - ], - [ - 1, - 3 - ], - [ - 3, - -3 - ], - [ - 5, - 3 - ] - ], - "samplesMs": [ - 108.31925200000114, - 100.84043599999859, - 104.50316100000055, - 107.42984599999909, - 100.70825699999841, - 101.80743399999847, - 103.03595800000039 - ], - "timingMs": { - "min": 100.70825699999841, - "median": 103.03595800000039, - "p95": 108.31925200000114, - "max": 108.31925200000114 - } - }, - "upstream": { - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - [ - 5, - 3 - ], - [ - 3, - -1 - ], - [ - 4, - -1 - ], - [ - 3, - -1 - ], - [ - 0, - -2 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 2, - 4 - ], - [ - 1, - 6 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 2, - 6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 57.842703999998776, - 52.539254999999685, - 62.86090399999739, - 62.943789000000834, - 63.994024000003265, - 58.06553100000019, - 69.60019099999772 - ], - "timingMs": { - "min": 52.539254999999685, - "median": 62.86090399999739, - "p95": 69.60019099999772, - "max": 69.60019099999772 - } - }, - "candidate": { - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - [ - 5, - 3 - ], - [ - 0, - -1 - ], - [ - 4, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - 4 - ], - [ - 3, - -2 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 1, - 6 - ], - [ - 2, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 4.143740999999864, - 4.183720999997604, - 5.502520000001823, - 4.291022999997949, - 4.217161999997188, - 5.547338000000309, - 4.5542700000005425 - ], - "timingMs": { - "min": 4.143740999999864, - "median": 4.291022999997949, - "p95": 5.547338000000309, - "max": 5.547338000000309 - } - }, - "baseline": { - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - [ - 5, - 3 - ], - [ - 0, - -1 - ], - [ - 4, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - 4 - ], - [ - 3, - -2 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 1, - 6 - ], - [ - 2, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 4.331283999999869, - 4.332555000000866, - 5.416350000003149, - 4.885149000001547, - 4.654962000000523, - 5.4907620000012685, - 4.518545999999333 - ], - "timingMs": { - "min": 4.331283999999869, - "median": 4.654962000000523, - "p95": 5.4907620000012685, - "max": 5.4907620000012685 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.9616466713713308, - "noiseRatio": 1.1489469707722866, - "lowerRatio": 0.9058638580267406, - "upperRatio": 1.0208645729326158 - }, - "timingAttempts": [ - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.9616466713713308, - "noiseRatio": 1.1489469707722866, - "lowerRatio": 0.9058638580267406, - "upperRatio": 1.0208645729326158, - "candidate": [ - 4.143740999999864, - 4.183720999997604, - 5.502520000001823, - 4.291022999997949, - 4.217161999997188, - 5.547338000000309, - 4.5542700000005425 - ], - "baseline": [ - 4.331283999999869, - 4.332555000000866, - 5.416350000003149, - 4.885149000001547, - 4.654962000000523, - 5.4907620000012685, - 4.518545999999333 - ], - "selfA": [ - 4.625722000000678, - 5.576132000001962, - 4.508424999999988, - 5.803915000000416, - 6.617059000000154, - 4.632231999999931, - 4.604619000001549 - ], - "selfB": [ - 4.773423999999068, - 5.871366999999736, - 4.550218000000314, - 4.410737999998673, - 6.91250299999956, - 4.636637999999948, - 4.632652000000235 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 4.625722000000678, - 5.576132000001962, - 4.508424999999988, - 5.803915000000416, - 6.617059000000154, - 4.632231999999931, - 4.604619000001549 - ], - "baselineSelfB": [ - 4.773423999999068, - 5.871366999999736, - 4.550218000000314, - 4.410737999998673, - 6.91250299999956, - 4.636637999999948, - 4.632652000000235 - ] - }, - "historicalReference": { - "A": 16, - "U": 6, - "C": 48, - "plateSwitches": 15, - "commands": [ - [ - 5, - 3 - ], - [ - 3, - -1 - ], - [ - 4, - -1 - ], - [ - 3, - -1 - ], - [ - 0, - -2 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 2, - 4 - ], - [ - 1, - 6 - ], - [ - 4, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - -3 - ], - [ - 2, - 6 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "raven-tower-floor2-chest-2", - "title": "Second chest on Raven Tower floor 2", - "optimumA": 8, - "solvers": { - "originalBfs": { - "A": 8, - "U": 5, - "C": 29, - "plateSwitches": 7, - "commands": [ - [ - 3, - 1 - ], - [ - 2, - -5 - ], - [ - 4, - 5 - ], - [ - 0, - -1 - ], - [ - 3, - -3 - ], - [ - 1, - 5 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ] - ], - "samplesMs": [ - 10.414410000001226, - 10.942296000001079, - 11.73520599999756, - 12.065078999999969, - 11.859881000000314, - 10.259762000001501, - 10.593574999998964 - ], - "timingMs": { - "min": 10.259762000001501, - "median": 10.942296000001079, - "p95": 12.065078999999969, - "max": 12.065078999999969 - } - }, - "upstream": { - "A": 9, - "U": 5, - "C": 27, - "plateSwitches": 8, - "commands": [ - [ - 2, - -4 - ], - [ - 4, - 4 - ], - [ - 2, - -4 - ], - [ - 1, - 2 - ], - [ - 4, - 4 - ], - [ - 3, - -2 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 0, - -1 - ] - ], - "samplesMs": [ - 3.3507239999999, - 3.5150829999984126, - 3.589153000000806, - 3.159175000000687, - 3.235450000000128, - 3.046875, - 3.1596759999993083 - ], - "timingMs": { - "min": 3.046875, - "median": 3.235450000000128, - "p95": 3.589153000000806, - "max": 3.589153000000806 - } - }, - "candidate": { - "A": 8, - "U": 5, - "C": 29, - "plateSwitches": 7, - "commands": [ - [ - 3, - 1 - ], - [ - 2, - -5 - ], - [ - 4, - 5 - ], - [ - 3, - -3 - ], - [ - 0, - -1 - ], - [ - 1, - 5 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ] - ], - "samplesMs": [ - 0.2090749999988475, - 0.18787299999894458, - 0.19018599999981234, - 0.19074800000089454, - 0.26812300000165123, - 0.18184399999881862, - 0.18494799999825773 - ], - "timingMs": { - "min": 0.18184399999881862, - "median": 0.19018599999981234, - "p95": 0.26812300000165123, - "max": 0.26812300000165123 - } - }, - "baseline": { - "A": 8, - "U": 5, - "C": 29, - "plateSwitches": 7, - "commands": [ - [ - 3, - 1 - ], - [ - 2, - -5 - ], - [ - 4, - 5 - ], - [ - 3, - -3 - ], - [ - 0, - -1 - ], - [ - 1, - 5 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ] - ], - "samplesMs": [ - 0.1737619999985327, - 0.38508100000035483, - 0.1844790000031935, - 0.18815299999914714, - 0.3753949999991164, - 0.17896900000050664, - 0.2280639999989944 - ], - "timingMs": { - "min": 0.1737619999985327, - "median": 0.18815299999914714, - "p95": 0.38508100000035483, - "max": 0.38508100000035483 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.8645695885706429, - "noiseRatio": 2.560384072894661, - "lowerRatio": 0.6275363684867805, - "upperRatio": 1.1911350656594766 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.8645695885706429, - "noiseRatio": 2.560384072894661, - "lowerRatio": 0.6275363684867805, - "upperRatio": 1.1911350656594766, - "candidate": [ - 0.2090749999988475, - 0.18787299999894458, - 0.19018599999981234, - 0.19074800000089454, - 0.26812300000165123, - 0.18184399999881862, - 0.18494799999825773 - ], - "baseline": [ - 0.1737619999985327, - 0.38508100000035483, - 0.1844790000031935, - 0.18815299999914714, - 0.3753949999991164, - 0.17896900000050664, - 0.2280639999989944 - ], - "selfA": [ - 0.15834900000118068, - 0.30418900000222493, - 0.1636560000006284, - 0.1498460000002524, - 0.13784800000212272, - 0.14803300000130548, - 0.14752299999963725 - ], - "selfB": [ - 0.3392410000014934, - 0.161603999997169, - 0.141392999998061, - 0.1488050000007206, - 0.4518319999988307, - 0.23185000000012224, - 0.2425459999976738 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.1174300209462606, - "noiseRatio": 1.4011723583337032, - "lowerRatio": 0.6735988577571576, - "upperRatio": 1.8536994790482817, - "candidate": [ - 0.1515880000006291, - 0.16059099999984028, - 0.18262500000128057, - 0.22522899999967194, - 0.22564899999997579, - 0.1519380000027013, - 0.14048099999854458 - ], - "baseline": [ - 0.16291499999715597, - 0.39866999999867403, - 0.14552899999762303, - 0.14259400000082678, - 0.1534910000009404, - 0.10282500000175787, - 0.1042170000000624 - ], - "selfA": [ - 0.14237400000274647, - 0.18588900000031572, - 0.14339599999948405, - 0.16595000000233995, - 0.16012100000079954, - 0.13953000000037719, - 0.11175799999909941 - ], - "selfB": [ - 0.17087700000047334, - 0.14751199999955134, - 0.13004600000203936, - 0.13298000000213506, - 0.11249899999893387, - 0.10684000000037486, - 0.10226400000101421 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.15834900000118068, - 0.30418900000222493, - 0.1636560000006284, - 0.1498460000002524, - 0.13784800000212272, - 0.14803300000130548, - 0.14752299999963725 - ], - "baselineSelfB": [ - 0.3392410000014934, - 0.161603999997169, - 0.141392999998061, - 0.1488050000007206, - 0.4518319999988307, - 0.23185000000012224, - 0.2425459999976738 - ] - }, - "historicalReference": { - "A": 9, - "U": 5, - "C": 27, - "plateSwitches": 8, - "commands": [ - [ - 2, - -4 - ], - [ - 4, - 4 - ], - [ - 2, - -4 - ], - [ - 1, - 2 - ], - [ - 4, - 4 - ], - [ - 3, - -2 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 0, - -1 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "regression", - "deltas": { - "A": -1, - "U": 0, - "C": 2, - "plateSwitches": -1 - }, - "regressions": [ - "C" - ] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "raven-tower-floor3-entrance-chest", - "title": "Chest at entrance (Raven Tower floor 3)", - "optimumA": 14, - "solvers": { - "originalBfs": { - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - [ - 1, - 1 - ], - [ - 3, - -5 - ], - [ - 4, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - 2 - ], - [ - 3, - -2 - ], - [ - 4, - -4 - ], - [ - 0, - -2 - ], - [ - 4, - -2 - ], - [ - 2, - -4 - ], - [ - 1, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 8.83679100000154, - 8.982130000000325, - 8.625953000002482, - 8.88793699999951, - 8.592082000002847, - 8.99558000000252, - 9.141379000000597 - ], - "timingMs": { - "min": 8.592082000002847, - "median": 8.88793699999951, - "p95": 9.141379000000597, - "max": 9.141379000000597 - } - }, - "upstream": { - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - [ - 1, - 1 - ], - [ - 4, - -2 - ], - [ - 3, - -1 - ], - [ - 0, - -2 - ], - [ - 1, - 2 - ], - [ - 4, - -4 - ], - [ - 0, - -2 - ], - [ - 4, - -2 - ], - [ - 3, - -6 - ], - [ - 2, - -4 - ], - [ - 1, - 6 - ], - [ - 4, - -6 - ], - [ - 3, - -3 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 6.582016000000294, - 6.546243000000686, - 6.528865999996924, - 6.855569000003015, - 6.925384000001941, - 6.393413000001601, - 6.751021999996738 - ], - "timingMs": { - "min": 6.393413000001601, - "median": 6.582016000000294, - "p95": 6.925384000001941, - "max": 6.925384000001941 - } - }, - "candidate": { - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - [ - 3, - -4 - ], - [ - 1, - 1 - ], - [ - 4, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - 2 - ], - [ - 4, - -4 - ], - [ - 2, - -4 - ], - [ - 1, - 3 - ], - [ - 3, - -6 - ], - [ - 4, - -3 - ], - [ - 0, - -3 - ], - [ - 1, - 3 - ], - [ - 4, - -5 - ], - [ - 0, - -2 - ] - ], - "samplesMs": [ - 0.7942550000007031, - 0.608486000000994, - 0.6061120000013034, - 0.6011739999994461, - 1.504503999996814, - 0.582656999999017, - 0.59052900000097 - ], - "timingMs": { - "min": 0.582656999999017, - "median": 0.6061120000013034, - "p95": 1.504503999996814, - "max": 1.504503999996814 - } - }, - "baseline": { - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - [ - 3, - -4 - ], - [ - 1, - 1 - ], - [ - 4, - -2 - ], - [ - 0, - -2 - ], - [ - 1, - 2 - ], - [ - 4, - -4 - ], - [ - 2, - -4 - ], - [ - 1, - 3 - ], - [ - 3, - -6 - ], - [ - 4, - -3 - ], - [ - 0, - -3 - ], - [ - 1, - 3 - ], - [ - 4, - -5 - ], - [ - 0, - -2 - ] - ], - "samplesMs": [ - 0.8380610000021989, - 0.6058720000000903, - 0.5996830000003683, - 0.5866229999992356, - 0.6243800000011106, - 0.6162979999971867, - 0.6453909999981988 - ], - "timingMs": { - "min": 0.5866229999992356, - "median": 0.6162979999971867, - "p95": 0.8380610000021989, - "max": 0.8380610000021989 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", - "ratio": 1.1083765061419144, - "noiseRatio": 1.3801452889981793, - "lowerRatio": 0.7717859784158841, - "upperRatio": 1.591760557621026 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.1083765061419144, - "noiseRatio": 1.3801452889981793, - "lowerRatio": 0.7717859784158841, - "upperRatio": 1.591760557621026, - "candidate": [ - 0.7942550000007031, - 0.608486000000994, - 0.6061120000013034, - 0.6011739999994461, - 1.504503999996814, - 0.582656999999017, - 0.59052900000097 - ], - "baseline": [ - 0.8380610000021989, - 0.6058720000000903, - 0.5996830000003683, - 0.5866229999992356, - 0.6243800000011106, - 0.6162979999971867, - 0.6453909999981988 - ], - "selfA": [ - 0.5778299999983574, - 0.5204529999973602, - 0.5887160000020231, - 0.5333229999996547, - 0.5577990000019781, - 0.5679250000030152, - 0.890410999996675 - ], - "selfB": [ - 0.5575390000012703, - 0.5334729999995034, - 0.5357269999985874, - 0.8581520000007004, - 0.6916309999978694, - 0.6502689999979339, - 0.643779000001814 - ] - }, - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.8973039120558461, - "noiseRatio": 1.2384243053733441, - "lowerRatio": 0.7396232112067196, - "upperRatio": 1.0886006528609207, - "candidate": [ - 0.58195600000181, - 0.6624369999990449, - 0.559783000000607, - 0.5499369999997725, - 0.5522510000009788, - 0.575896999998804, - 0.5872239999989688 - ], - "baseline": [ - 0.7072239999979502, - 0.5310200000021723, - 0.7234690000004775, - 0.7314110000006622, - 0.579001000001881, - 0.7183319999967352, - 0.5725610000008601 - ], - "selfA": [ - 0.5859620000010182, - 0.4981999999981781, - 1.043922000000748, - 0.5956460000015795, - 0.5555260000000999, - 0.588064999999915, - 0.5505690000027244 - ], - "selfB": [ - 0.5465320000002976, - 0.693213000002288, - 0.8042210000021441, - 0.5980500000005122, - 0.5295069999992847, - 0.518289999999979, - 0.5515699999996286 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.5778299999983574, - 0.5204529999973602, - 0.5887160000020231, - 0.5333229999996547, - 0.5577990000019781, - 0.5679250000030152, - 0.890410999996675 - ], - "baselineSelfB": [ - 0.5575390000012703, - 0.5334729999995034, - 0.5357269999985874, - 0.8581520000007004, - 0.6916309999978694, - 0.6502689999979339, - 0.643779000001814 - ] - }, - "historicalReference": { - "A": 14, - "U": 5, - "C": 44, - "plateSwitches": 13, - "commands": [ - [ - 1, - 1 - ], - [ - 4, - -2 - ], - [ - 3, - -1 - ], - [ - 0, - -2 - ], - [ - 1, - 2 - ], - [ - 4, - -4 - ], - [ - 0, - -2 - ], - [ - 4, - -2 - ], - [ - 3, - -6 - ], - [ - 2, - -4 - ], - [ - 1, - 6 - ], - [ - 4, - -6 - ], - [ - 3, - -3 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "raven-tower-floor3-empty-chest", - "title": "Empty chest (Raven Tower floor 3)", - "optimumA": 9, - "solvers": { - "originalBfs": { - "A": 9, - "U": 5, - "C": 41, - "plateSwitches": 8, - "commands": [ - [ - 0, - -2 - ], - [ - 3, - 2 - ], - [ - 4, - 4 - ], - [ - 2, - 6 - ], - [ - 0, - -6 - ], - [ - 4, - 6 - ], - [ - 3, - -6 - ], - [ - 4, - 6 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 8.191428999998607, - 7.88301599999977, - 8.246202000002086, - 7.704927000002499, - 7.983926999997493, - 7.844818000001396, - 8.416607999999542 - ], - "timingMs": { - "min": 7.704927000002499, - "median": 7.983926999997493, - "p95": 8.416607999999542, - "max": 8.416607999999542 - } - }, - "upstream": { - "A": 9, - "U": 5, - "C": 37, - "plateSwitches": 8, - "commands": [ - [ - 4, - 6 - ], - [ - 2, - 2 - ], - [ - 0, - -4 - ], - [ - 2, - 4 - ], - [ - 4, - 6 - ], - [ - 0, - -4 - ], - [ - 3, - -4 - ], - [ - 4, - 4 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 5.718185999998241, - 5.81905800000095, - 5.573308000002726, - 5.475751000001765, - 5.505826999997225, - 6.900445999999647, - 6.195044000000053 - ], - "timingMs": { - "min": 5.475751000001765, - "median": 5.718185999998241, - "p95": 6.900445999999647, - "max": 6.900445999999647 - } - }, - "candidate": { - "A": 9, - "U": 5, - "C": 37, - "plateSwitches": 8, - "commands": [ - [ - 0, - -2 - ], - [ - 4, - 6 - ], - [ - 2, - 3 - ], - [ - 1, - -3 - ], - [ - 4, - 6 - ], - [ - 3, - -4 - ], - [ - 0, - -6 - ], - [ - 4, - 4 - ], - [ - 2, - 3 - ] - ], - "samplesMs": [ - 0.08624999999665306, - 0.08687099999951897, - 0.07469199999832199, - 0.07137700000021141, - 0.07719599999836646, - 0.1030350000000908, - 0.07694600000104401 - ], - "timingMs": { - "min": 0.07137700000021141, - "median": 0.07719599999836646, - "p95": 0.1030350000000908, - "max": 0.1030350000000908 - } - }, - "baseline": { - "A": 9, - "U": 5, - "C": 37, - "plateSwitches": 8, - "commands": [ - [ - 0, - -2 - ], - [ - 4, - 6 - ], - [ - 2, - 3 - ], - [ - 1, - -3 - ], - [ - 4, - 6 - ], - [ - 3, - -4 - ], - [ - 0, - -6 - ], - [ - 4, - 4 - ], - [ - 2, - 3 - ] - ], - "samplesMs": [ - 0.08483799999885377, - 0.07612399999925401, - 0.06979499999943073, - 0.06724199999734992, - 0.0774859999983164, - 0.15070700000069337, - 0.0695449999984703 - ], - "timingMs": { - "min": 0.06724199999734992, - "median": 0.07612399999925401, - "p95": 0.15070700000069337, - "max": 0.15070700000069337 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.9990243084827689, - "noiseRatio": 1.2588706040281896, - "lowerRatio": 0.8327126185582548, - "upperRatio": 1.1985522336235057 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9990243084827689, - "noiseRatio": 1.2588706040281896, - "lowerRatio": 0.8327126185582548, - "upperRatio": 1.1985522336235057, - "candidate": [ - 0.08624999999665306, - 0.08687099999951897, - 0.07469199999832199, - 0.07137700000021141, - 0.07719599999836646, - 0.1030350000000908, - 0.07694600000104401 - ], - "baseline": [ - 0.08483799999885377, - 0.07612399999925401, - 0.06979499999943073, - 0.06724199999734992, - 0.0774859999983164, - 0.15070700000069337, - 0.0695449999984703 - ], - "selfA": [ - 0.04280500000095344, - 0.03637499999967986, - 0.03615400000126101, - 0.0432650000002468, - 0.0386579999976675, - 0.03601400000115973, - 0.053790999998454936 - ], - "selfB": [ - 0.04246399999829009, - 0.037256000003253575, - 0.037186000001383945, - 0.03613500000210479, - 0.03869799999665702, - 0.03599400000166497, - 0.0370760000005248 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.205806775312928, - "noiseRatio": 1.7397850216411979, - "lowerRatio": 1.0386565433715693, - "upperRatio": 1.3998563708758325, - "candidate": [ - 0.06960400000025402, - 0.059549000001425156, - 0.05339000000094529, - 0.04511799999818322, - 0.04377599999861559, - 0.042392999999719905, - 0.044366999998601386 - ], - "baseline": [ - 0.05088700000123936, - 0.03899900000033085, - 0.04384600000048522, - 0.04312500000014552, - 0.040050999999948544, - 0.04046100000050501, - 0.036475000000791624 - ], - "selfA": [ - 0.10278499999913038, - 0.04206300000078045, - 0.04592899999988731, - 0.04229300000224612, - 0.03649500000028638, - 0.038207000001420965, - 0.06284400000004098 - ], - "selfB": [ - 0.045658999999432126, - 0.0559240000002319, - 0.06819200000245473, - 0.04268400000000838, - 0.03617400000075577, - 0.035823999998683576, - 0.03702599999814993 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.04280500000095344, - 0.03637499999967986, - 0.03615400000126101, - 0.0432650000002468, - 0.0386579999976675, - 0.03601400000115973, - 0.053790999998454936 - ], - "baselineSelfB": [ - 0.04246399999829009, - 0.037256000003253575, - 0.037186000001383945, - 0.03613500000210479, - 0.03869799999665702, - 0.03599400000166497, - 0.0370760000005248 - ] - }, - "historicalReference": { - "A": 9, - "U": 5, - "C": 37, - "plateSwitches": 8, - "commands": [ - [ - 4, - 6 - ], - [ - 2, - 2 - ], - [ - 0, - -4 - ], - [ - 2, - 4 - ], - [ - 4, - 6 - ], - [ - 0, - -4 - ], - [ - 3, - -4 - ], - [ - 4, - 4 - ], - [ - 1, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": -4, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "raven-tower-floor4-elixirs-chest", - "title": "Elixir chest (Raven Tower floor 4)", - "optimumA": 8, - "solvers": { - "originalBfs": { - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - [ - 1, - -5 - ], - [ - 2, - -5 - ], - [ - 1, - -5 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ], - [ - 2, - -6 - ], - [ - 1, - -3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 83.17834399999992, - 74.13739599999826, - 74.50477899999896, - 75.47908599999937, - 76.50029300000097, - 78.36638099999982, - 71.84072899999956 - ], - "timingMs": { - "min": 71.84072899999956, - "median": 75.47908599999937, - "p95": 83.17834399999992, - "max": 83.17834399999992 - } - }, - "upstream": { - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - [ - 1, - -4 - ], - [ - 2, - -5 - ], - [ - 4, - 6 - ], - [ - 1, - -6 - ], - [ - 2, - -6 - ], - [ - 3, - 3 - ], - [ - 1, - -3 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 110.47301000000152, - 114.14526299999852, - 110.3698000000004, - 114.36231000000043, - 111.61005700000169, - 121.12372700000196, - 109.82555099999809 - ], - "timingMs": { - "min": 109.82555099999809, - "median": 111.61005700000169, - "p95": 121.12372700000196, - "max": 121.12372700000196 - } - }, - "candidate": { - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - [ - 1, - -5 - ], - [ - 2, - -5 - ], - [ - 1, - -5 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ], - [ - 2, - -6 - ], - [ - 1, - -3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.10121200000139652, - 0.09749600000213832, - 0.09930900000108522, - 0.1320479999994859, - 0.1046169999972335, - 0.10208399999828544, - 0.1787189999995462 - ], - "timingMs": { - "min": 0.09749600000213832, - "median": 0.10208399999828544, - "p95": 0.1787189999995462, - "max": 0.1787189999995462 - } - }, - "baseline": { - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - [ - 1, - -5 - ], - [ - 2, - -5 - ], - [ - 1, - -5 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ], - [ - 2, - -6 - ], - [ - 1, - -3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.17902999999932945, - 0.1783289999984845, - 0.18321600000126637, - 0.20663100000092527, - 0.09565400000064983, - 0.09541299999909825, - 0.3788600000007136 - ], - "timingMs": { - "min": 0.09541299999909825, - "median": 0.17902999999932945, - "p95": 0.3788600000007136, - "max": 0.3788600000007136 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.6675911082749053, - "noiseRatio": 1.184396583079744, - "lowerRatio": 0.46656469874395085, - "upperRatio": 0.9552327663184453 - }, - "timingAttempts": [ - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.6675911082749053, - "noiseRatio": 1.184396583079744, - "lowerRatio": 0.46656469874395085, - "upperRatio": 0.9552327663184453, - "candidate": [ - 0.10121200000139652, - 0.09749600000213832, - 0.09930900000108522, - 0.1320479999994859, - 0.1046169999972335, - 0.10208399999828544, - 0.1787189999995462 - ], - "baseline": [ - 0.17902999999932945, - 0.1783289999984845, - 0.18321600000126637, - 0.20663100000092527, - 0.09565400000064983, - 0.09541299999909825, - 0.3788600000007136 - ], - "selfA": [ - 0.04361500000231899, - 0.06065099999977974, - 0.037575999998807674, - 0.03660500000114553, - 0.03620500000033644, - 0.03787699999884353, - 0.04167299999971874 - ], - "selfB": [ - 0.0431639999987965, - 0.04461700000319979, - 0.03866800000105286, - 0.03752600000007078, - 0.03514299999733339, - 0.0369460000001709, - 0.04143300000214367 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.04361500000231899, - 0.06065099999977974, - 0.037575999998807674, - 0.03660500000114553, - 0.03620500000033644, - 0.03787699999884353, - 0.04167299999971874 - ], - "baselineSelfB": [ - 0.0431639999987965, - 0.04461700000319979, - 0.03866800000105286, - 0.03752600000007078, - 0.03514299999733339, - 0.0369460000001709, - 0.04143300000214367 - ] - }, - "historicalReference": { - "A": 8, - "U": 5, - "C": 36, - "plateSwitches": 7, - "commands": [ - [ - 1, - -4 - ], - [ - 2, - -5 - ], - [ - 4, - 6 - ], - [ - 1, - -6 - ], - [ - 2, - -6 - ], - [ - 3, - 3 - ], - [ - 1, - -3 - ], - [ - 0, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "raven-tower-floor4-food-chest", - "title": "Food chest (Raven Tower floor 4)", - "optimumA": 11, - "solvers": { - "originalBfs": { - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - [ - 2, - 4 - ], - [ - 0, - 4 - ], - [ - 2, - 4 - ], - [ - 3, - -1 - ], - [ - 4, - 6 - ], - [ - 0, - 5 - ], - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - 2 - ], - [ - 5, - -3 - ] - ], - "samplesMs": [ - 97.7792389999995, - 91.33732400000008, - 92.23392299999978, - 91.68276400000104, - 102.99014899999747, - 95.99610599999869, - 99.89724299999943 - ], - "timingMs": { - "min": 91.33732400000008, - "median": 95.99610599999869, - "p95": 102.99014899999747, - "max": 102.99014899999747 - } - }, - "upstream": { - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - [ - 2, - 2 - ], - [ - 5, - -3 - ], - [ - 4, - 2 - ], - [ - 1, - 2 - ], - [ - 3, - -4 - ], - [ - 2, - 6 - ], - [ - 0, - 6 - ], - [ - 4, - 6 - ], - [ - 2, - 6 - ], - [ - 1, - 3 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 54.11020300000018, - 57.60958899999969, - 61.53917400000137, - 55.85695200000191, - 61.156958999999915, - 53.834318000001076, - 59.46566299999904 - ], - "timingMs": { - "min": 53.834318000001076, - "median": 57.60958899999969, - "p95": 61.53917400000137, - "max": 61.53917400000137 - } - }, - "candidate": { - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - [ - 2, - 4 - ], - [ - 0, - 4 - ], - [ - 2, - 4 - ], - [ - 4, - 6 - ], - [ - 0, - 5 - ], - [ - 3, - -1 - ], - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - 2 - ], - [ - 5, - -3 - ] - ], - "samplesMs": [ - 0.2506780000003346, - 0.2718989999993937, - 0.254493000000366, - 0.2518290000007255, - 0.25441200000204844, - 0.25262999999904423, - 0.25344099999711034 - ], - "timingMs": { - "min": 0.2506780000003346, - "median": 0.25344099999711034, - "p95": 0.2718989999993937, - "max": 0.2718989999993937 - } - }, - "baseline": { - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - [ - 2, - 4 - ], - [ - 0, - 4 - ], - [ - 2, - 4 - ], - [ - 4, - 6 - ], - [ - 0, - 5 - ], - [ - 3, - -1 - ], - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 3, - -3 - ], - [ - 4, - 2 - ], - [ - 5, - -3 - ] - ], - "samplesMs": [ - 0.253590999996959, - 0.2544530000013765, - 0.3543040000004112, - 0.3376579999967362, - 0.689728000001196, - 0.24406699999963166, - 0.2806319999981497 - ], - "timingMs": { - "min": 0.24406699999963166, - "median": 0.2806319999981497, - "p95": 0.689728000001196, - "max": 0.689728000001196 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.791794191123639, - "noiseRatio": 1.2867074792823932, - "lowerRatio": 0.5366177466775199, - "upperRatio": 1.1683140279628057 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.791794191123639, - "noiseRatio": 1.2867074792823932, - "lowerRatio": 0.5366177466775199, - "upperRatio": 1.1683140279628057, - "candidate": [ - 0.2506780000003346, - 0.2718989999993937, - 0.254493000000366, - 0.2518290000007255, - 0.25441200000204844, - 0.25262999999904423, - 0.25344099999711034 - ], - "baseline": [ - 0.253590999996959, - 0.2544530000013765, - 0.3543040000004112, - 0.3376579999967362, - 0.689728000001196, - 0.24406699999963166, - 0.2806319999981497 - ], - "selfA": [ - 0.2361849999979313, - 0.24300600000060513, - 0.20763200000146753, - 0.15654599999834318, - 0.17571500000121887, - 0.19390200000270852, - 0.18893499999830965 - ], - "selfB": [ - 0.21192900000096415, - 0.2919690000016999, - 0.1893549999986135, - 0.16495800000120653, - 0.18872399999963818, - 0.28813399999853573, - 0.16666000000259373 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.2835505202555417, - "noiseRatio": 1.1576822667634536, - "lowerRatio": 0.609052941215962, - "upperRatio": 2.7050225465770965, - "candidate": [ - 0.20972600000095554, - 0.2006519999995362, - 0.20491899999979069, - 0.190335999999661, - 1.4210389999971085, - 0.21012599999812664, - 0.205749000000651 - ], - "baseline": [ - 0.2024440000022878, - 0.24982600000294042, - 0.1870710000002873, - 0.21022699999957695, - 0.2277429999994638, - 0.17295000000012806, - 0.22424799999862444 - ], - "selfA": [ - 0.18246500000168453, - 0.16624100000262843, - 0.20588900000075228, - 0.16185400000176742, - 0.2058599999982107, - 0.1571670000012091, - 0.19158900000184076 - ], - "selfB": [ - 0.20850399999835645, - 0.1572569999989355, - 0.1819839999989199, - 0.18515900000056718, - 0.2207219999982044, - 0.15332100000159699, - 0.15602399999988847 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.2361849999979313, - 0.24300600000060513, - 0.20763200000146753, - 0.15654599999834318, - 0.17571500000121887, - 0.19390200000270852, - 0.18893499999830965 - ], - "baselineSelfB": [ - 0.21192900000096415, - 0.2919690000016999, - 0.1893549999986135, - 0.16495800000120653, - 0.18872399999963818, - 0.28813399999853573, - 0.16666000000259373 - ] - }, - "historicalReference": { - "A": 11, - "U": 6, - "C": 43, - "plateSwitches": 10, - "commands": [ - [ - 2, - 2 - ], - [ - 5, - -3 - ], - [ - 4, - 2 - ], - [ - 1, - 2 - ], - [ - 3, - -4 - ], - [ - 2, - 6 - ], - [ - 0, - 6 - ], - [ - 4, - 6 - ], - [ - 2, - 6 - ], - [ - 1, - 3 - ], - [ - 0, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "raven-tower-top-chest", - "title": "Chest at the top of Raven Tower", - "optimumA": 20, - "solvers": { - "originalBfs": { - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - [ - 1, - -3 - ], - [ - 3, - 1 - ], - [ - 0, - -1 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - -1 - ], - [ - 4, - -2 - ], - [ - 3, - 2 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - -6 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 5, - -4 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 92.66402200000084, - 85.79478199999721, - 82.07782800000132, - 83.04535400000168, - 84.04145399999834, - 79.77004399999714, - 83.32641700000022 - ], - "timingMs": { - "min": 79.77004399999714, - "median": 83.32641700000022, - "p95": 92.66402200000084, - "max": 92.66402200000084 - } - }, - "upstream": { - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - [ - 3, - 1 - ], - [ - 0, - -1 - ], - [ - 4, - -2 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 3, - 1 - ], - [ - 0, - -1 - ], - [ - 3, - 1 - ], - [ - 5, - -4 - ], - [ - 2, - -2 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - -6 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 46.34331300000122, - 50.413792999999714, - 48.378396000000066, - 45.21305199999915, - 47.07778799999869, - 45.144538000000466, - 45.60020400000212 - ], - "timingMs": { - "min": 45.144538000000466, - "median": 46.34331300000122, - "p95": 50.413792999999714, - "max": 50.413792999999714 - } - }, - "candidate": { - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - [ - 1, - -3 - ], - [ - 3, - 1 - ], - [ - 0, - -1 - ], - [ - 4, - -2 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 3, - 2 - ], - [ - 5, - -4 - ], - [ - 2, - -1 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - -6 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 8.81014999999752, - 9.386878000001161, - 7.199506000000838, - 7.277092000000266, - 8.341165000001638, - 7.16088800000216, - 7.3677390000011656 - ], - "timingMs": { - "min": 7.16088800000216, - "median": 7.3677390000011656, - "p95": 9.386878000001161, - "max": 9.386878000001161 - } - }, - "baseline": { - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - [ - 1, - -3 - ], - [ - 3, - 1 - ], - [ - 0, - -1 - ], - [ - 4, - -2 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 3, - 2 - ], - [ - 5, - -4 - ], - [ - 2, - -1 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - -6 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 7.32115899999917, - 7.708662999997614, - 7.653169000001071, - 7.451024999998481, - 7.424524999998539, - 7.58204200000182, - 7.2477880000005825 - ], - "timingMs": { - "min": 7.2477880000005825, - "median": 7.451024999998481, - "p95": 7.708662999997614, - "max": 7.708662999997614 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: passed. Conflicting verdicts remain inconclusive.", - "ratio": 1.054739439857676, - "noiseRatio": 1.1587383305418397, - "lowerRatio": 0.9383082947744821, - "upperRatio": 1.1856180875590174 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.054739439857676, - "noiseRatio": 1.1587383305418397, - "lowerRatio": 0.9383082947744821, - "upperRatio": 1.1856180875590174, - "candidate": [ - 8.81014999999752, - 9.386878000001161, - 7.199506000000838, - 7.277092000000266, - 8.341165000001638, - 7.16088800000216, - 7.3677390000011656 - ], - "baseline": [ - 7.32115899999917, - 7.708662999997614, - 7.653169000001071, - 7.451024999998481, - 7.424524999998539, - 7.58204200000182, - 7.2477880000005825 - ], - "selfA": [ - 10.304070000001957, - 7.409732000000076, - 7.386548000002222, - 7.220076999998128, - 7.574179999999615, - 7.289161000000604, - 7.3406479999976 - ], - "selfB": [ - 7.7767449999992095, - 7.701472000000649, - 7.5592670000005455, - 7.3004779999973834, - 7.607731000000058, - 7.475791000000754, - 7.181249000001117 - ] - }, - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.9182661984217206, - "noiseRatio": 1.1770345018327855, - "lowerRatio": 0.8390194121206238, - "upperRatio": 1.004997976188246, - "candidate": [ - 6.679714000001695, - 6.669749000000593, - 6.973034999999072, - 7.089050000002317, - 6.658572000000277, - 6.836018999998487, - 7.377133000001777 - ], - "baseline": [ - 8.675147000001743, - 7.396783000000141, - 7.555703000001813, - 7.18513399999938, - 7.0869779999993625, - 7.374659000000975, - 7.377963999999338 - ], - "selfA": [ - 7.881371999999828, - 7.177472999999736, - 7.4805989999986195, - 7.079416000000492, - 7.387920000001031, - 7.338244999999006, - 9.866481000000931 - ], - "selfB": [ - 7.339386999999988, - 7.687040000000707, - 7.126546999999846, - 7.171593999999459, - 7.120296999997663, - 7.5404189999972004, - 7.502402000001894 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 10.304070000001957, - 7.409732000000076, - 7.386548000002222, - 7.220076999998128, - 7.574179999999615, - 7.289161000000604, - 7.3406479999976 - ], - "baselineSelfB": [ - 7.7767449999992095, - 7.701472000000649, - 7.5592670000005455, - 7.3004779999973834, - 7.607731000000058, - 7.475791000000754, - 7.181249000001117 - ] - }, - "historicalReference": { - "A": 20, - "U": 6, - "C": 48, - "plateSwitches": 19, - "commands": [ - [ - 3, - 1 - ], - [ - 0, - -1 - ], - [ - 4, - -2 - ], - [ - 2, - -1 - ], - [ - 0, - -1 - ], - [ - 3, - 1 - ], - [ - 0, - -1 - ], - [ - 3, - 1 - ], - [ - 5, - -4 - ], - [ - 2, - -2 - ], - [ - 0, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 4, - -6 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "fire-lizard-cave-chest", - "title": "Fire lizard cave box", - "optimumA": 10, - "solvers": { - "originalBfs": { - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - [ - 0, - -3 - ], - [ - 2, - -5 - ], - [ - 4, - 4 - ], - [ - 1, - 3 - ], - [ - 5, - -5 - ], - [ - 0, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - 6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 65.79064300000027, - 68.84199600000284, - 77.1776269999973, - 66.0915359999999, - 66.05964799999856, - 67.79507999999987, - 67.62880000000223 - ], - "timingMs": { - "min": 65.79064300000027, - "median": 67.62880000000223, - "p95": 77.1776269999973, - "max": 77.1776269999973 - } - }, - "upstream": { - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - [ - 0, - -3 - ], - [ - 2, - -5 - ], - [ - 5, - -5 - ], - [ - 1, - 3 - ], - [ - 4, - 4 - ], - [ - 0, - -6 - ], - [ - 4, - 6 - ], - [ - 3, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 54.80736099999922, - 57.56618399999934, - 52.73060600000099, - 58.65374099999826, - 56.06977199999892, - 57.91260599999805, - 51.749800000001414 - ], - "timingMs": { - "min": 51.749800000001414, - "median": 56.06977199999892, - "p95": 58.65374099999826, - "max": 58.65374099999826 - } - }, - "candidate": { - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - [ - 0, - -3 - ], - [ - 2, - -5 - ], - [ - 4, - 4 - ], - [ - 1, - 3 - ], - [ - 5, - -5 - ], - [ - 0, - -6 - ], - [ - 3, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - 6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 0.15909000000101514, - 0.154592999999295, - 0.23011599999881582, - 0.21219900000141934, - 0.16016099999978906, - 0.165168999999878, - 0.15812799999912386 - ], - "timingMs": { - "min": 0.154592999999295, - "median": 0.16016099999978906, - "p95": 0.23011599999881582, - "max": 0.23011599999881582 - } - }, - "baseline": { - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - [ - 0, - -3 - ], - [ - 2, - -5 - ], - [ - 4, - 4 - ], - [ - 1, - 3 - ], - [ - 5, - -5 - ], - [ - 0, - -6 - ], - [ - 3, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - 6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 0.2369170000019949, - 0.14187399999718764, - 0.1439460000001418, - 0.8497690000003786, - 0.2489949999981036, - 0.1520989999989979, - 0.14818300000115414 - ], - "timingMs": { - "min": 0.14187399999718764, - "median": 0.1520989999989979, - "p95": 0.8497690000003786, - "max": 0.8497690000003786 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.804293537589002, - "noiseRatio": 1.3254050811839404, - "lowerRatio": 0.4270201739792324, - "upperRatio": 1.5148888367014062 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.804293537589002, - "noiseRatio": 1.3254050811839404, - "lowerRatio": 0.4270201739792324, - "upperRatio": 1.5148888367014062, - "candidate": [ - 0.15909000000101514, - 0.154592999999295, - 0.23011599999881582, - 0.21219900000141934, - 0.16016099999978906, - 0.165168999999878, - 0.15812799999912386 - ], - "baseline": [ - 0.2369170000019949, - 0.14187399999718764, - 0.1439460000001418, - 0.8497690000003786, - 0.2489949999981036, - 0.1520989999989979, - 0.14818300000115414 - ], - "selfA": [ - 0.08253400000103284, - 0.07786699999996927, - 0.07955999999830965, - 0.0776369999985036, - 0.07868900000175927, - 0.07694500000070548, - 0.07762600000205566 - ], - "selfB": [ - 0.0803410000007716, - 0.07866899999862653, - 0.1316079999996873, - 0.08522799999991548, - 0.0790290000004461, - 0.07833799999934854, - 0.07584400000268943 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.141551086004325, - "noiseRatio": 1.286651420752876, - "lowerRatio": 0.9099141786903442, - "upperRatio": 1.4321558147750646, - "candidate": [ - 0.09527300000263494, - 0.11260899999979301, - 0.2097460000004503, - 0.17250999999669148, - 0.3272929999984626, - 0.17456200000015087, - 0.2067719999977271 - ], - "baseline": [ - 0.1129800000016985, - 0.08226300000023912, - 0.17137799999909475, - 0.17301100000258884, - 0.21042700000180048, - 0.18348599999808357, - 0.17061699999976554 - ], - "selfA": [ - 0.10280499999862514, - 0.07885899999746471, - 0.24328700000114623, - 0.1657100000011269, - 0.1849079999992682, - 0.16883400000006077, - 0.17228999999861117 - ], - "selfB": [ - 0.09511299999940093, - 0.09594500000093831, - 0.16367600000012317, - 0.1932410000008531, - 0.18885399999999208, - 0.14862400000129128, - 0.15884899999946356 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.08253400000103284, - 0.07786699999996927, - 0.07955999999830965, - 0.0776369999985036, - 0.07868900000175927, - 0.07694500000070548, - 0.07762600000205566 - ], - "baselineSelfB": [ - 0.0803410000007716, - 0.07866899999862653, - 0.1316079999996873, - 0.08522799999991548, - 0.0790290000004461, - 0.07833799999934854, - 0.07584400000268943 - ] - }, - "historicalReference": { - "A": 10, - "U": 6, - "C": 41, - "plateSwitches": 9, - "commands": [ - [ - 0, - -3 - ], - [ - 2, - -5 - ], - [ - 5, - -5 - ], - [ - 1, - 3 - ], - [ - 4, - 4 - ], - [ - 0, - -6 - ], - [ - 4, - 6 - ], - [ - 3, - -3 - ], - [ - 2, - -3 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "silas-tavern-basement-chest", - "title": "Silas tavern basement chest", - "optimumA": 9, - "solvers": { - "originalBfs": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 2 - ], - [ - 2, - 2 - ], - [ - 1, - -2 - ], - [ - 3, - -4 - ], - [ - 4, - 6 - ], - [ - 2, - 5 - ], - [ - 3, - -5 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ] - ], - "samplesMs": [ - 7.5152410000009695, - 6.635957999998936, - 7.778768000000127, - 6.651370999999926, - 6.911211999999068, - 6.966225000000122, - 6.363377000001492 - ], - "timingMs": { - "min": 6.363377000001492, - "median": 6.911211999999068, - "p95": 7.778768000000127, - "max": 7.778768000000127 - } - }, - "upstream": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 4, - 2 - ], - [ - 0, - 2 - ], - [ - 2, - 4 - ], - [ - 1, - -1 - ], - [ - 3, - -6 - ], - [ - 4, - 6 - ], - [ - 2, - 3 - ], - [ - 3, - -3 - ], - [ - 1, - -3 - ] - ], - "samplesMs": [ - 4.033581000003323, - 4.462597000001551, - 4.053450999999768, - 3.9988890000022366, - 4.383406999997533, - 4.350818999999319, - 4.036565999998857 - ], - "timingMs": { - "min": 3.9988890000022366, - "median": 4.053450999999768, - "p95": 4.462597000001551, - "max": 4.462597000001551 - } - }, - "candidate": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 2 - ], - [ - 4, - 2 - ], - [ - 2, - 4 - ], - [ - 3, - -3 - ], - [ - 1, - -4 - ], - [ - 3, - -3 - ], - [ - 4, - 6 - ], - [ - 2, - 3 - ], - [ - 3, - -3 - ] - ], - "samplesMs": [ - 0.09969999999884749, - 0.09260899999935646, - 0.23263000000224565, - 0.09626500000013039, - 0.19647600000098464, - 0.12190299999929266, - 0.09030600000187405 - ], - "timingMs": { - "min": 0.09030600000187405, - "median": 0.09969999999884749, - "p95": 0.23263000000224565, - "max": 0.23263000000224565 - } - }, - "baseline": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 0, - 2 - ], - [ - 4, - 2 - ], - [ - 2, - 4 - ], - [ - 3, - -3 - ], - [ - 1, - -4 - ], - [ - 3, - -3 - ], - [ - 4, - 6 - ], - [ - 2, - 3 - ], - [ - 3, - -3 - ] - ], - "samplesMs": [ - 0.09748599999875296, - 0.08798300000125892, - 0.0884330000008049, - 0.10627999999996973, - 0.10178399999858811, - 0.08879399999932502, - 0.08768199999758508 - ], - "timingMs": { - "min": 0.08768199999758508, - "median": 0.08879399999932502, - "p95": 0.10627999999996973, - "max": 0.10627999999996973 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.320488556379239, - "noiseRatio": 1.180032118378632, - "lowerRatio": 0.8714691763276781, - "upperRatio": 2.0008625375326963 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.320488556379239, - "noiseRatio": 1.180032118378632, - "lowerRatio": 0.8714691763276781, - "upperRatio": 2.0008625375326963, - "candidate": [ - 0.09969999999884749, - 0.09260899999935646, - 0.23263000000224565, - 0.09626500000013039, - 0.19647600000098464, - 0.12190299999929266, - 0.09030600000187405 - ], - "baseline": [ - 0.09748599999875296, - 0.08798300000125892, - 0.0884330000008049, - 0.10627999999996973, - 0.10178399999858811, - 0.08879399999932502, - 0.08768199999758508 - ], - "selfA": [ - 0.10103200000230572, - 0.09945000000152504, - 0.09373100000084378, - 0.09153800000058254, - 0.10363599999982398, - 0.09454199999890989, - 0.09767700000156765 - ], - "selfB": [ - 0.14004100000238395, - 0.10149299999829964, - 0.08953499999915948, - 0.09194800000113901, - 0.0951630000017758, - 0.09010499999931199, - 0.09333100000003469 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.173680563745284, - "noiseRatio": 1.3486853435922972, - "lowerRatio": 0.9446854795201814, - "upperRatio": 1.4581848621332805, - "candidate": [ - 0.06841300000087358, - 0.07091600000057952, - 0.06845400000020163, - 0.0600799999992887, - 0.05476199999975506, - 0.08401700000104029, - 0.07028599999830476 - ], - "baseline": [ - 0.07296900000073947, - 0.05095599999913247, - 0.0671409999995376, - 0.05230899999878602, - 0.050645999999687774, - 0.04899400000067544, - 0.06490699999994831 - ], - "selfA": [ - 0.0682730000007723, - 0.048923000002105255, - 0.05582399999912013, - 0.05029599999761558, - 0.04884400000082678, - 0.05027499999778229, - 0.11249899999893387 - ], - "selfB": [ - 0.0546119999999064, - 0.04967399999804911, - 0.05346099999951548, - 0.05699600000298233, - 0.049043999999412335, - 0.056735999998636544, - 0.06904399999984889 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.10103200000230572, - 0.09945000000152504, - 0.09373100000084378, - 0.09153800000058254, - 0.10363599999982398, - 0.09454199999890989, - 0.09767700000156765 - ], - "baselineSelfB": [ - 0.14004100000238395, - 0.10149299999829964, - 0.08953499999915948, - 0.09194800000113901, - 0.0951630000017758, - 0.09010499999931199, - 0.09333100000003469 - ] - }, - "historicalReference": { - "A": 9, - "U": 5, - "C": 30, - "plateSwitches": 8, - "commands": [ - [ - 4, - 2 - ], - [ - 0, - 2 - ], - [ - 2, - 4 - ], - [ - 1, - -1 - ], - [ - 3, - -6 - ], - [ - 4, - 6 - ], - [ - 2, - 3 - ], - [ - 3, - -3 - ], - [ - 1, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "silas-tavern-storeroom-chest", - "title": "Tavern storeroom chest", - "optimumA": 9, - "solvers": { - "originalBfs": { - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - -3 - ], - [ - 3, - 4 - ], - [ - 5, - -6 - ], - [ - 0, - 2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -6 - ], - [ - 3, - 3 - ], - [ - 5, - -3 - ] - ], - "samplesMs": [ - 86.35269200000039, - 86.39734999999928, - 87.22469300000012, - 86.90162799999962, - 92.51974999999948, - 93.56100699999661, - 89.50045800000225 - ], - "timingMs": { - "min": 86.35269200000039, - "median": 87.22469300000012, - "p95": 93.56100699999661, - "max": 93.56100699999661 - } - }, - "upstream": { - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - -3 - ], - [ - 5, - -6 - ], - [ - 3, - 4 - ], - [ - 2, - -6 - ], - [ - 4, - 2 - ], - [ - 1, - -2 - ], - [ - 5, - -3 - ], - [ - 3, - 3 - ], - [ - 0, - 2 - ] - ], - "samplesMs": [ - 30.261741000002075, - 30.76007000000027, - 31.442306000000826, - 30.83804699999746, - 30.93786800000089, - 31.911792999999307, - 30.919159999997646 - ], - "timingMs": { - "min": 30.261741000002075, - "median": 30.919159999997646, - "p95": 31.911792999999307, - "max": 31.911792999999307 - } - }, - "candidate": { - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - -3 - ], - [ - 3, - 4 - ], - [ - 5, - -6 - ], - [ - 0, - 2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -6 - ], - [ - 3, - 3 - ], - [ - 5, - -3 - ] - ], - "samplesMs": [ - 0.07831699999951525, - 0.08786300000065239, - 0.08873400000084075, - 0.08549800000037067, - 0.07897899999807123, - 0.15704600000026403, - 0.08667099999729544 - ], - "timingMs": { - "min": 0.07831699999951525, - "median": 0.08667099999729544, - "p95": 0.15704600000026403, - "max": 0.15704600000026403 - } - }, - "baseline": { - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - -3 - ], - [ - 3, - 4 - ], - [ - 5, - -6 - ], - [ - 0, - 2 - ], - [ - 1, - -2 - ], - [ - 4, - 2 - ], - [ - 2, - -6 - ], - [ - 3, - 3 - ], - [ - 5, - -3 - ] - ], - "samplesMs": [ - 0.0747029999984079, - 0.10290400000303634, - 0.09423199999946519, - 0.07635500000105822, - 0.07498299999861047, - 0.07961000000068452, - 0.09744700000010198 - ], - "timingMs": { - "min": 0.0747029999984079, - "median": 0.07961000000068452, - "p95": 0.10290400000303634, - "max": 0.10290400000303634 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.0827207798614293, - "noiseRatio": 1.2755939756219734, - "lowerRatio": 0.8053439182441593, - "upperRatio": 1.455631886684635 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0827207798614293, - "noiseRatio": 1.2755939756219734, - "lowerRatio": 0.8053439182441593, - "upperRatio": 1.455631886684635, - "candidate": [ - 0.07831699999951525, - 0.08786300000065239, - 0.08873400000084075, - 0.08549800000037067, - 0.07897899999807123, - 0.15704600000026403, - 0.08667099999729544 - ], - "baseline": [ - 0.0747029999984079, - 0.10290400000303634, - 0.09423199999946519, - 0.07635500000105822, - 0.07498299999861047, - 0.07961000000068452, - 0.09744700000010198 - ], - "selfA": [ - 0.06413600000087172, - 0.035683999998582294, - 0.03358999999909429, - 0.040950999999040505, - 0.03813799999988987, - 0.03569399999832967, - 0.0344920000025013 - ], - "selfB": [ - 0.0454280000012659, - 0.03895899999770336, - 0.03473200000007637, - 0.03296000000045751, - 0.03528299999743467, - 0.03343999999924563, - 0.033501000001706416 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.3444701627735123, - "noiseRatio": 1.326360716338602, - "lowerRatio": 1.1064743393154834, - "upperRatio": 1.6336574237288684, - "candidate": [ - 0.06225300000369316, - 0.04447700000309851, - 0.04084100000181934, - 0.04450699999870267, - 0.0705870000019786, - 0.05752599999686936, - 0.03737600000022212 - ], - "baseline": [ - 0.04410599999391707, - 0.0357640000001993, - 0.037176000005274545, - 0.03373099999589613, - 0.03996099999494618, - 0.03532299999642419, - 0.034451999999873806 - ], - "selfA": [ - 0.03779699999722652, - 0.03780700000061188, - 0.06295500000123866, - 0.03412100000423379, - 0.03629499999806285, - 0.03231899999809684, - 0.03559300000051735 - ], - "selfB": [ - 0.03267000000050757, - 0.03413200000068173, - 0.04069100000197068, - 0.032167999997909646, - 0.032859999999345746, - 0.033250000000407454, - 0.032448999998450745 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.06413600000087172, - 0.035683999998582294, - 0.03358999999909429, - 0.040950999999040505, - 0.03813799999988987, - 0.03569399999832967, - 0.0344920000025013 - ], - "baselineSelfB": [ - 0.0454280000012659, - 0.03895899999770336, - 0.03473200000007637, - 0.03296000000045751, - 0.03528299999743467, - 0.03343999999924563, - 0.033501000001706416 - ] - }, - "historicalReference": { - "A": 9, - "U": 6, - "C": 31, - "plateSwitches": 8, - "commands": [ - [ - 2, - -3 - ], - [ - 5, - -6 - ], - [ - 3, - 4 - ], - [ - 2, - -6 - ], - [ - 4, - 2 - ], - [ - 1, - -2 - ], - [ - 5, - -3 - ], - [ - 3, - 3 - ], - [ - 0, - 2 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "silas-tavern-storeroom-door", - "title": "Tavern storeroom door", - "optimumA": 10, - "solvers": { - "originalBfs": { - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - [ - 3, - 2 - ], - [ - 5, - -3 - ], - [ - 1, - -3 - ], - [ - 5, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - 3 - ], - [ - 5, - -3 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 2, - -3 - ] - ], - "samplesMs": [ - 80.11304299999756, - 77.77798000000621, - 78.00689499999862, - 78.69170499999746, - 76.83120600000257, - 80.71428799999558, - 85.26358299999993 - ], - "timingMs": { - "min": 76.83120600000257, - "median": 78.69170499999746, - "p95": 85.26358299999993, - "max": 85.26358299999993 - } - }, - "upstream": { - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - [ - 3, - 2 - ], - [ - 5, - -3 - ], - [ - 1, - -3 - ], - [ - 5, - -3 - ], - [ - 2, - -3 - ], - [ - 5, - -3 - ], - [ - 4, - 3 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 2, - -3 - ] - ], - "samplesMs": [ - 23.187386999998125, - 23.839638999997987, - 23.705205999998725, - 22.879604000001564, - 23.425035000000207, - 27.695729000006395, - 23.709653000005346 - ], - "timingMs": { - "min": 22.879604000001564, - "median": 23.705205999998725, - "p95": 27.695729000006395, - "max": 27.695729000006395 - } - }, - "candidate": { - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - [ - 3, - 2 - ], - [ - 5, - -3 - ], - [ - 1, - -3 - ], - [ - 5, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - 3 - ], - [ - 5, - -3 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 2, - -3 - ] - ], - "samplesMs": [ - 0.16059099999984028, - 0.17933999999513617, - 0.15847800000483403, - 0.14888400000199908, - 0.1567060000015772, - 0.15615400000388036, - 0.15922999999747844 - ], - "timingMs": { - "min": 0.14888400000199908, - "median": 0.15847800000483403, - "p95": 0.17933999999513617, - "max": 0.17933999999513617 - } - }, - "baseline": { - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - [ - 3, - 2 - ], - [ - 5, - -3 - ], - [ - 1, - -3 - ], - [ - 5, - -3 - ], - [ - 2, - -3 - ], - [ - 4, - 3 - ], - [ - 5, - -3 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 2, - -3 - ] - ], - "samplesMs": [ - 0.14888300000166055, - 0.1705560000045807, - 0.1828449999957229, - 0.14683099999820115, - 0.15048699999897508, - 0.17150800000672461, - 0.1691149999969639 - ], - "timingMs": { - "min": 0.14683099999820115, - "median": 0.1691149999969639, - "p95": 0.1828449999957229, - "max": 0.1828449999957229 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.9834611811726517, - "noiseRatio": 1.1271243069466848, - "lowerRatio": 0.901740013471515, - "upperRatio": 1.0725884184178547 - }, - "timingAttempts": [ - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.9834611811726517, - "noiseRatio": 1.1271243069466848, - "lowerRatio": 0.901740013471515, - "upperRatio": 1.0725884184178547, - "candidate": [ - 0.16059099999984028, - 0.17933999999513617, - 0.15847800000483403, - 0.14888400000199908, - 0.1567060000015772, - 0.15615400000388036, - 0.15922999999747844 - ], - "baseline": [ - 0.14888300000166055, - 0.1705560000045807, - 0.1828449999957229, - 0.14683099999820115, - 0.15048699999897508, - 0.17150800000672461, - 0.1691149999969639 - ], - "selfA": [ - 0.0891540000011446, - 0.0846880000026431, - 0.08241399999678833, - 0.081372000000556, - 0.10190299999521812, - 0.08347499999945285, - 0.08200300000316929 - ], - "selfB": [ - 0.08291399999870919, - 0.08349599999928614, - 0.09510299999965355, - 0.08360600000014529, - 0.08433700000023236, - 0.0831560000005993, - 0.0832659999941825 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.0891540000011446, - 0.0846880000026431, - 0.08241399999678833, - 0.081372000000556, - 0.10190299999521812, - 0.08347499999945285, - 0.08200300000316929 - ], - "baselineSelfB": [ - 0.08291399999870919, - 0.08349599999928614, - 0.09510299999965355, - 0.08360600000014529, - 0.08433700000023236, - 0.0831560000005993, - 0.0832659999941825 - ] - }, - "historicalReference": { - "A": 10, - "U": 5, - "C": 31, - "plateSwitches": 9, - "commands": [ - [ - 3, - 2 - ], - [ - 5, - -3 - ], - [ - 1, - -3 - ], - [ - 5, - -3 - ], - [ - 2, - -3 - ], - [ - 5, - -3 - ], - [ - 4, - 3 - ], - [ - 1, - -4 - ], - [ - 5, - -4 - ], - [ - 2, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "jan-pond-house-chest", - "title": "Hut chest opposite Jan (New Camp)", - "optimumA": 8, - "solvers": { - "originalBfs": { - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - [ - 0, - -4 - ], - [ - 2, - -6 - ], - [ - 3, - 4 - ], - [ - 4, - 3 - ], - [ - 5, - -3 - ], - [ - 3, - 5 - ], - [ - 5, - -5 - ], - [ - 1, - 3 - ] - ], - "samplesMs": [ - 45.542411000002176, - 45.81365899999946, - 51.588528000000224, - 44.50916600000346, - 45.30073699999775, - 46.15811699999904, - 58.21535800000129 - ], - "timingMs": { - "min": 44.50916600000346, - "median": 45.81365899999946, - "p95": 58.21535800000129, - "max": 58.21535800000129 - } - }, - "upstream": { - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - [ - 3, - 4 - ], - [ - 5, - -3 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ], - [ - 3, - 5 - ], - [ - 5, - -5 - ], - [ - 0, - -4 - ], - [ - 1, - 3 - ] - ], - "samplesMs": [ - 51.14832399999432, - 52.83220799999981, - 51.68855800000165, - 51.66298900000402, - 56.487758999996004, - 51.530539999999746, - 51.48268800000369 - ], - "timingMs": { - "min": 51.14832399999432, - "median": 51.66298900000402, - "p95": 56.487758999996004, - "max": 56.487758999996004 - } - }, - "candidate": { - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - [ - 0, - -4 - ], - [ - 2, - -6 - ], - [ - 3, - 4 - ], - [ - 4, - 3 - ], - [ - 5, - -3 - ], - [ - 3, - 5 - ], - [ - 5, - -5 - ], - [ - 1, - 3 - ] - ], - "samplesMs": [ - 0.0833760000023176, - 0.08456700000533601, - 0.09170799999992596, - 0.08654999999998836, - 0.08488800000486663, - 0.08460699999704957, - 0.08499799999844981 - ], - "timingMs": { - "min": 0.0833760000023176, - "median": 0.08488800000486663, - "p95": 0.09170799999992596, - "max": 0.09170799999992596 - } - }, - "baseline": { - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - [ - 0, - -4 - ], - [ - 2, - -6 - ], - [ - 3, - 4 - ], - [ - 4, - 3 - ], - [ - 5, - -3 - ], - [ - 3, - 5 - ], - [ - 5, - -5 - ], - [ - 1, - 3 - ] - ], - "samplesMs": [ - 0.09716599999956088, - 0.0753130000011879, - 0.0742720000052941, - 0.07391099999949802, - 0.09722600000532111, - 0.07622499999706633, - 0.07314999999653082 - ], - "timingMs": { - "min": 0.07314999999653082, - "median": 0.0753130000011879, - "p95": 0.09722600000532111, - "max": 0.09722600000532111 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.0664434884312919, - "noiseRatio": 1.842180308877821, - "lowerRatio": 0.9143097756984165, - "upperRatio": 1.2438910140151889 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0664434884312919, - "noiseRatio": 1.842180308877821, - "lowerRatio": 0.9143097756984165, - "upperRatio": 1.2438910140151889, - "candidate": [ - 0.0833760000023176, - 0.08456700000533601, - 0.09170799999992596, - 0.08654999999998836, - 0.08488800000486663, - 0.08460699999704957, - 0.08499799999844981 - ], - "baseline": [ - 0.09716599999956088, - 0.0753130000011879, - 0.0742720000052941, - 0.07391099999949802, - 0.09722600000532111, - 0.07622499999706633, - 0.07314999999653082 - ], - "selfA": [ - 0.03774600000178907, - 0.0322189999933471, - 0.033620000001974404, - 0.03025599999818951, - 0.05900900000415277, - 0.04835300000559073, - 0.03581399999529822 - ], - "selfB": [ - 0.033230000000912696, - 0.032117999995534774, - 0.0320480000009411, - 0.03303999999479856, - 0.032859999999345746, - 0.15161799999623327, - 0.03429099999630125 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.1575456486374698, - "noiseRatio": 1.729128773037858, - "lowerRatio": 0.8125389147078709, - "upperRatio": 1.649043392784793, - "candidate": [ - 0.10885400000552181, - 0.04756100000668084, - 0.04406600000220351, - 0.03858799999579787, - 0.03550299999915296, - 0.03853800000069896, - 0.1106770000042161 - ], - "baseline": [ - 0.14290500000061002, - 0.04235299999709241, - 0.03642499999841675, - 0.03477300000668038, - 0.03739599999971688, - 0.03447199999936856, - 0.04844299999967916 - ], - "selfA": [ - 0.10960500000510365, - 0.03544399999373127, - 0.04343499999959022, - 0.03728599999885773, - 0.033740999999281485, - 0.03304999999818392, - 0.05491200000687968 - ], - "selfB": [ - 0.04831299999932526, - 0.03614399999787565, - 0.045968999998876825, - 0.034292000003915746, - 0.033320000002277084, - 0.05176799999753712, - 0.03471200000058161 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.03774600000178907, - 0.0322189999933471, - 0.033620000001974404, - 0.03025599999818951, - 0.05900900000415277, - 0.04835300000559073, - 0.03581399999529822 - ], - "baselineSelfB": [ - 0.033230000000912696, - 0.032117999995534774, - 0.0320480000009411, - 0.03303999999479856, - 0.032859999999345746, - 0.15161799999623327, - 0.03429099999630125 - ] - }, - "historicalReference": { - "A": 8, - "U": 6, - "C": 33, - "plateSwitches": 7, - "commands": [ - [ - 3, - 4 - ], - [ - 5, - -3 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ], - [ - 3, - 5 - ], - [ - 5, - -5 - ], - [ - 0, - -4 - ], - [ - 1, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "graham-chest", - "title": "Graham's chest", - "optimumA": 10, - "solvers": { - "originalBfs": { - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - [ - 1, - 5 - ], - [ - 4, - 6 - ], - [ - 0, - -5 - ], - [ - 1, - 6 - ], - [ - 4, - 5 - ], - [ - 1, - 5 - ], - [ - 2, - 2 - ], - [ - 3, - -3 - ], - [ - 4, - 2 - ], - [ - 5, - -1 - ] - ], - "samplesMs": [ - 97.51188099999854, - 100.20344099999784, - 97.98475300000428, - 97.99732100000256, - 97.42808399999922, - 99.88392000000022, - 111.1799899999969 - ], - "timingMs": { - "min": 97.42808399999922, - "median": 97.99732100000256, - "p95": 111.1799899999969, - "max": 111.1799899999969 - } - }, - "upstream": { - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - [ - 5, - -1 - ], - [ - 1, - 6 - ], - [ - 4, - 6 - ], - [ - 1, - 5 - ], - [ - 0, - -5 - ], - [ - 4, - 5 - ], - [ - 3, - -3 - ], - [ - 1, - 5 - ], - [ - 4, - 2 - ], - [ - 2, - 2 - ] - ], - "samplesMs": [ - 30.595291999998153, - 31.713304000004428, - 30.877466999998433, - 31.12507899999764, - 31.41895099999965, - 31.13688700000057, - 32.48808999999892 - ], - "timingMs": { - "min": 30.595291999998153, - "median": 31.13688700000057, - "p95": 32.48808999999892, - "max": 32.48808999999892 - } - }, - "candidate": { - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - [ - 1, - 5 - ], - [ - 4, - 6 - ], - [ - 0, - -5 - ], - [ - 1, - 6 - ], - [ - 4, - 5 - ], - [ - 1, - 5 - ], - [ - 2, - 2 - ], - [ - 3, - -3 - ], - [ - 4, - 2 - ], - [ - 5, - -1 - ] - ], - "samplesMs": [ - 0.09199899999657646, - 0.09075699999812059, - 0.09119700000155717, - 0.09116799999901559, - 0.09144799999921815, - 0.08807200000592275, - 0.09309999999823049 - ], - "timingMs": { - "min": 0.08807200000592275, - "median": 0.09119700000155717, - "p95": 0.09309999999823049, - "max": 0.09309999999823049 - } - }, - "baseline": { - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - [ - 1, - 5 - ], - [ - 4, - 6 - ], - [ - 0, - -5 - ], - [ - 1, - 6 - ], - [ - 4, - 5 - ], - [ - 1, - 5 - ], - [ - 2, - 2 - ], - [ - 3, - -3 - ], - [ - 4, - 2 - ], - [ - 5, - -1 - ] - ], - "samplesMs": [ - 0.08312499999738066, - 0.0822039999984554, - 0.08281500000157394, - 0.0991989999965881, - 0.08289399999921443, - 0.08142300000326941, - 0.08016000000498025 - ], - "timingMs": { - "min": 0.08016000000498025, - "median": 0.08281500000157394, - "p95": 0.0991989999965881, - "max": 0.0991989999965881 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.0800045710243187, - "noiseRatio": 1.049300965877078, - "lowerRatio": 0.9988650879646944, - "upperRatio": 1.1677351501093312 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0800045710243187, - "noiseRatio": 1.049300965877078, - "lowerRatio": 0.9988650879646944, - "upperRatio": 1.1677351501093312, - "candidate": [ - 0.09199899999657646, - 0.09075699999812059, - 0.09119700000155717, - 0.09116799999901559, - 0.09144799999921815, - 0.08807200000592275, - 0.09309999999823049 - ], - "baseline": [ - 0.08312499999738066, - 0.0822039999984554, - 0.08281500000157394, - 0.0991989999965881, - 0.08289399999921443, - 0.08142300000326941, - 0.08016000000498025 - ], - "selfA": [ - 0.044937999999092426, - 0.03505299999960698, - 0.035894000000553206, - 0.03513300000486197, - 0.03538300000218442, - 0.03479199999856064, - 0.035112000005028676 - ], - "selfB": [ - 0.044115999997302424, - 0.03825700000015786, - 0.034681999997701496, - 0.0341820000030566, - 0.035862999997334555, - 0.03529300000082003, - 0.03511300000536721 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.968048234600079, - "noiseRatio": 2.265419728250408, - "lowerRatio": 0.7026228026645848, - "upperRatio": 1.333741775755728, - "candidate": [ - 0.04287399999884656, - 0.05362099999911152, - 0.05537299999559764, - 0.04906399999890709, - 0.047962000004190486, - 0.048112999997101724, - 0.045178000000305474 - ], - "baseline": [ - 0.04477699999551987, - 0.03931900000316091, - 0.05411100000492297, - 0.08370599999761907, - 0.04502700000011828, - 0.0365349999992759, - 0.06230399999913061 - ], - "selfA": [ - 0.037486000001081266, - 0.0357640000001993, - 0.04923399999825051, - 0.05193800000415649, - 0.03757700000278419, - 0.03607400000328198, - 0.04441699999733828 - ], - "selfB": [ - 0.07458200000110082, - 0.03622500000346918, - 0.17764699999679578, - 0.04055200000584591, - 0.03741699999955017, - 0.03860800000256859, - 0.04309500000090338 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.044937999999092426, - 0.03505299999960698, - 0.035894000000553206, - 0.03513300000486197, - 0.03538300000218442, - 0.03479199999856064, - 0.035112000005028676 - ], - "baselineSelfB": [ - 0.044115999997302424, - 0.03825700000015786, - 0.034681999997701496, - 0.0341820000030566, - 0.035862999997334555, - 0.03529300000082003, - 0.03511300000536721 - ] - }, - "historicalReference": { - "A": 10, - "U": 6, - "C": 40, - "plateSwitches": 9, - "commands": [ - [ - 5, - -1 - ], - [ - 1, - 6 - ], - [ - 4, - 6 - ], - [ - 1, - 5 - ], - [ - 0, - -5 - ], - [ - 4, - 5 - ], - [ - 3, - -3 - ], - [ - 1, - 5 - ], - [ - 4, - 2 - ], - [ - 2, - 2 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "gravo-chest", - "title": "Gravo's chest", - "optimumA": 8, - "solvers": { - "originalBfs": { - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - [ - 0, - -5 - ], - [ - 4, - -1 - ], - [ - 1, - 3 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 3, - 3 - ], - [ - 4, - -6 - ], - [ - 1, - 3 - ] - ], - "samplesMs": [ - 5.060563999999431, - 6.205956999998307, - 5.36050499999692, - 6.018434999998135, - 5.078320000000531, - 6.04823999999644, - 5.1621760000052745 - ], - "timingMs": { - "min": 5.060563999999431, - "median": 5.36050499999692, - "p95": 6.205956999998307, - "max": 6.205956999998307 - } - }, - "upstream": { - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - [ - 4, - -1 - ], - [ - 1, - 3 - ], - [ - 0, - -5 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 4, - -6 - ], - [ - 3, - 3 - ], - [ - 1, - 3 - ] - ], - "samplesMs": [ - 5.411072000002605, - 5.99046300000191, - 5.337070000001404, - 5.183919000002788, - 5.227435000000696, - 5.3317320000060135, - 5.57223299999896 - ], - "timingMs": { - "min": 5.183919000002788, - "median": 5.337070000001404, - "p95": 5.99046300000191, - "max": 5.99046300000191 - } - }, - "candidate": { - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - [ - 0, - -5 - ], - [ - 4, - -1 - ], - [ - 1, - 3 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 3, - 3 - ], - [ - 4, - -6 - ], - [ - 1, - 3 - ] - ], - "samplesMs": [ - 0.05555399999866495, - 0.052237999996577855, - 0.06302500000310829, - 0.07672599999932572, - 0.0808919999981299, - 0.05266000000119675, - 0.06613899999501882 - ], - "timingMs": { - "min": 0.052237999996577855, - "median": 0.06302500000310829, - "p95": 0.0808919999981299, - "max": 0.0808919999981299 - } - }, - "baseline": { - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - [ - 0, - -5 - ], - [ - 4, - -1 - ], - [ - 1, - 3 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 3, - 3 - ], - [ - 4, - -6 - ], - [ - 1, - 3 - ] - ], - "samplesMs": [ - 0.05351100000552833, - 0.050565999998070765, - 0.06857399999717018, - 0.0709869999991497, - 0.05471199999738019, - 0.05368000000453321, - 0.06633000000147149 - ], - "timingMs": { - "min": 0.050565999998070765, - "median": 0.05471199999738019, - "p95": 0.0709869999991497, - "max": 0.0709869999991497 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.063707752424634, - "noiseRatio": 2.743891970316831, - "lowerRatio": 0.9050448704312891, - "upperRatio": 1.2501857306025885 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.063707752424634, - "noiseRatio": 2.743891970316831, - "lowerRatio": 0.9050448704312891, - "upperRatio": 1.2501857306025885, - "candidate": [ - 0.05555399999866495, - 0.052237999996577855, - 0.06302500000310829, - 0.07672599999932572, - 0.0808919999981299, - 0.05266000000119675, - 0.06613899999501882 - ], - "baseline": [ - 0.05351100000552833, - 0.050565999998070765, - 0.06857399999717018, - 0.0709869999991497, - 0.05471199999738019, - 0.05368000000453321, - 0.06633000000147149 - ], - "selfA": [ - 0.02703099999780534, - 0.03684600000269711, - 0.03110700000252109, - 0.04055099999823142, - 0.03117700000439072, - 0.060490999996545725, - 0.03038599999854341 - ], - "selfB": [ - 0.03544300000066869, - 0.030075000002398156, - 0.027602000001934357, - 0.2703669999973499, - 0.030645999999251217, - 0.03443200000037905, - 0.027862000002642162 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0248126118746486, - "noiseRatio": 1.1235945098222313, - "lowerRatio": 0.805498042529257, - "upperRatio": 1.3038403993628487, - "candidate": [ - 0.03431099999579601, - 0.037606999998388346, - 0.03167799999937415, - 0.032669000000169035, - 0.034120999996957835, - 0.03345100000296952, - 0.029673999997612555 - ], - "baseline": [ - 0.05167800000344869, - 0.02889399999548914, - 0.03154700000595767, - 0.03694599999289494, - 0.028712999999697786, - 0.028151999998954125, - 0.02708100000018021 - ], - "selfA": [ - 0.03289000000222586, - 0.02740099999937229, - 0.03069600000162609, - 0.028783000001567416, - 0.02708100000018021, - 0.02731100000528386, - 0.02778099999704864 - ], - "selfB": [ - 0.026760999993712176, - 0.02837300000101095, - 0.031186000000161584, - 0.031217000003380235, - 0.026129000005312264, - 0.027701999999408145, - 0.026639999996405095 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.02703099999780534, - 0.03684600000269711, - 0.03110700000252109, - 0.04055099999823142, - 0.03117700000439072, - 0.060490999996545725, - 0.03038599999854341 - ], - "baselineSelfB": [ - 0.03544300000066869, - 0.030075000002398156, - 0.027602000001934357, - 0.2703669999973499, - 0.030645999999251217, - 0.03443200000037905, - 0.027862000002642162 - ] - }, - "historicalReference": { - "A": 8, - "U": 5, - "C": 27, - "plateSwitches": 7, - "commands": [ - [ - 4, - -1 - ], - [ - 1, - 3 - ], - [ - 0, - -5 - ], - [ - 2, - -3 - ], - [ - 1, - 3 - ], - [ - 4, - -6 - ], - [ - 3, - 3 - ], - [ - 1, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "diego-chest", - "title": "Diego's chest", - "optimumA": 11, - "solvers": { - "originalBfs": { - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - [ - 0, - 2 - ], - [ - 1, - 1 - ], - [ - 2, - -6 - ], - [ - 3, - -1 - ], - [ - 1, - 3 - ], - [ - 4, - 3 - ], - [ - 2, - -6 - ], - [ - 5, - 3 - ], - [ - 1, - 6 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ] - ], - "samplesMs": [ - 78.02613399999973, - 81.06815999999526, - 84.98362199999974, - 79.10918500000116, - 81.41457500000251, - 83.85891399999673, - 84.8131499999945 - ], - "timingMs": { - "min": 78.02613399999973, - "median": 81.41457500000251, - "p95": 84.98362199999974, - "max": 84.98362199999974 - } - }, - "upstream": { - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - [ - 1, - 1 - ], - [ - 3, - -1 - ], - [ - 2, - -6 - ], - [ - 1, - 3 - ], - [ - 4, - 3 - ], - [ - 5, - 3 - ], - [ - 2, - -6 - ], - [ - 1, - 6 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ], - [ - 0, - 2 - ] - ], - "samplesMs": [ - 32.76997400000255, - 31.144458000002487, - 47.63711399999738, - 39.21390999999858, - 30.63828300000023, - 31.230564999998023, - 31.128260000004957 - ], - "timingMs": { - "min": 30.63828300000023, - "median": 31.230564999998023, - "p95": 47.63711399999738, - "max": 47.63711399999738 - } - }, - "candidate": { - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - [ - 0, - 2 - ], - [ - 1, - 1 - ], - [ - 2, - -6 - ], - [ - 3, - -1 - ], - [ - 1, - 3 - ], - [ - 4, - 3 - ], - [ - 2, - -6 - ], - [ - 5, - 3 - ], - [ - 1, - 6 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ] - ], - "samplesMs": [ - 0.1474820000003092, - 0.1472009999997681, - 0.14838400000007823, - 0.14461799999844516, - 0.17976099999941653, - 0.14545899999939138, - 0.14338600000337465 - ], - "timingMs": { - "min": 0.14338600000337465, - "median": 0.1472009999997681, - "p95": 0.17976099999941653, - "max": 0.17976099999941653 - } - }, - "baseline": { - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - [ - 0, - 2 - ], - [ - 1, - 1 - ], - [ - 2, - -6 - ], - [ - 3, - -1 - ], - [ - 1, - 3 - ], - [ - 4, - 3 - ], - [ - 2, - -6 - ], - [ - 5, - 3 - ], - [ - 1, - 6 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ] - ], - "samplesMs": [ - 0.17654599999514176, - 0.1535310000035679, - 0.13709600000584032, - 0.1633559999972931, - 0.13717600000381935, - 0.14910400000371737, - 0.13600399999995716 - ], - "timingMs": { - "min": 0.13600399999995716, - "median": 0.14910400000371737, - "p95": 0.17654599999514176, - "max": 0.17654599999514176 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 1.0048369183719719, - "noiseRatio": 1.218232763484805, - "lowerRatio": 0.8602364107765427, - "upperRatio": 1.1737438916492953 - }, - "timingAttempts": [ - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 1.0048369183719719, - "noiseRatio": 1.218232763484805, - "lowerRatio": 0.8602364107765427, - "upperRatio": 1.1737438916492953, - "candidate": [ - 0.1474820000003092, - 0.1472009999997681, - 0.14838400000007823, - 0.14461799999844516, - 0.17976099999941653, - 0.14545899999939138, - 0.14338600000337465 - ], - "baseline": [ - 0.17654599999514176, - 0.1535310000035679, - 0.13709600000584032, - 0.1633559999972931, - 0.13717600000381935, - 0.14910400000371737, - 0.13600399999995716 - ], - "selfA": [ - 0.08095199999661418, - 0.07380099999863887, - 0.07164799999736715, - 0.07334100000298349, - 0.07056599999486934, - 0.08489800000097603, - 0.09023599999636644 - ], - "selfB": [ - 0.07590399999753572, - 0.07343100000434788, - 0.08940499999880558, - 0.07465200000297045, - 0.0691339999975753, - 0.07150700000056531, - 0.11747599999944214 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.08095199999661418, - 0.07380099999863887, - 0.07164799999736715, - 0.07334100000298349, - 0.07056599999486934, - 0.08489800000097603, - 0.09023599999636644 - ], - "baselineSelfB": [ - 0.07590399999753572, - 0.07343100000434788, - 0.08940499999880558, - 0.07465200000297045, - 0.0691339999975753, - 0.07150700000056531, - 0.11747599999944214 - ] - }, - "historicalReference": { - "A": 11, - "U": 6, - "C": 40, - "plateSwitches": 10, - "commands": [ - [ - 1, - 1 - ], - [ - 3, - -1 - ], - [ - 2, - -6 - ], - [ - 1, - 3 - ], - [ - 4, - 3 - ], - [ - 5, - 3 - ], - [ - 2, - -6 - ], - [ - 1, - 6 - ], - [ - 2, - -6 - ], - [ - 4, - 3 - ], - [ - 0, - 2 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "grim-chest", - "title": "Grim's chest", - "optimumA": 8, - "solvers": { - "originalBfs": { - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - [ - 0, - -4 - ], - [ - 1, - 5 - ], - [ - 3, - 5 - ], - [ - 0, - -2 - ], - [ - 1, - 1 - ], - [ - 2, - -1 - ], - [ - 4, - -2 - ], - [ - 5, - -1 - ] - ], - "samplesMs": [ - 123.99482600000192, - 106.83830999999918, - 110.33234799999627, - 106.63469299999997, - 106.61467199999606, - 108.50407700000505, - 107.13202199999796 - ], - "timingMs": { - "min": 106.61467199999606, - "median": 107.13202199999796, - "p95": 123.99482600000192, - "max": 123.99482600000192 - } - }, - "upstream": { - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - [ - 1, - 4 - ], - [ - 0, - -3 - ], - [ - 3, - 5 - ], - [ - 5, - -1 - ], - [ - 4, - -2 - ], - [ - 2, - -1 - ], - [ - 1, - 2 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 22.178500999994867, - 22.13407400000142, - 22.90026599999692, - 22.155064999999013, - 23.2216499999995, - 22.792263000003004, - 23.271536000000197 - ], - "timingMs": { - "min": 22.13407400000142, - "median": 22.792263000003004, - "p95": 23.271536000000197, - "max": 23.271536000000197 - } - }, - "candidate": { - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - [ - 4, - -2 - ], - [ - 3, - 1 - ], - [ - 1, - 6 - ], - [ - 2, - -1 - ], - [ - 0, - -5 - ], - [ - 3, - 4 - ], - [ - 0, - -1 - ], - [ - 5, - -1 - ] - ], - "samplesMs": [ - 0.2403919999997015, - 0.22331600000325125, - 0.19663600000058068, - 0.2736520000034943, - 0.19669700000667945, - 0.22057199999835575, - 0.1844280000004801 - ], - "timingMs": { - "min": 0.1844280000004801, - "median": 0.22057199999835575, - "p95": 0.2736520000034943, - "max": 0.2736520000034943 - } - }, - "baseline": { - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - [ - 4, - -2 - ], - [ - 3, - 1 - ], - [ - 1, - 6 - ], - [ - 2, - -1 - ], - [ - 0, - -5 - ], - [ - 3, - 4 - ], - [ - 0, - -1 - ], - [ - 5, - -1 - ] - ], - "samplesMs": [ - 0.2888749999983702, - 0.18909500000154367, - 0.18198399999528192, - 0.20887399999628542, - 0.20872399999643676, - 0.18437700000504265, - 0.19606499999645166 - ], - "timingMs": { - "min": 0.18198399999528192, - "median": 0.19606499999645166, - "p95": 0.2888749999983702, - "max": 0.2888749999983702 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.0571268346755425, - "noiseRatio": 1.5388577923341638, - "lowerRatio": 0.8915404357448891, - "upperRatio": 1.2534677057664099 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0571268346755425, - "noiseRatio": 1.5388577923341638, - "lowerRatio": 0.8915404357448891, - "upperRatio": 1.2534677057664099, - "candidate": [ - 0.2403919999997015, - 0.22331600000325125, - 0.19663600000058068, - 0.2736520000034943, - 0.19669700000667945, - 0.22057199999835575, - 0.1844280000004801 - ], - "baseline": [ - 0.2888749999983702, - 0.18909500000154367, - 0.18198399999528192, - 0.20887399999628542, - 0.20872399999643676, - 0.18437700000504265, - 0.19606499999645166 - ], - "selfA": [ - 0.1466710000022431, - 0.17328100000304403, - 0.1293149999983143, - 0.1080530000035651, - 0.10702100000344217, - 0.20902399999613408, - 0.18371599999954924 - ], - "selfB": [ - 0.13409200000023702, - 0.11385100000188686, - 0.1254289999997127, - 0.11034600000130013, - 0.10189399999944726, - 0.11756600000080653, - 0.14673100000072736 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.2244016265779087, - "noiseRatio": 1.5237638252159087, - "lowerRatio": 0.9993279779577071, - "upperRatio": 1.5001674887862242, - "candidate": [ - 0.22357699999702163, - 0.19327999999950407, - 0.14245500000106404, - 0.12996600000042235, - 0.1362550000048941, - 0.2415739999996731, - 0.1392889999988256 - ], - "baseline": [ - 0.17612499999813735, - 0.13331999999354593, - 0.12535899999784306, - 0.12398699999903329, - 0.13057699999626493, - 0.14042099999642232, - 0.1328700000012759 - ], - "selfA": [ - 0.13919999999779975, - 0.3052499999976135, - 0.14546899999550078, - 0.11716600000363542, - 0.12280499999906169, - 0.12243400000443216, - 0.14620999999897322 - ], - "selfB": [ - 0.15113800000108313, - 0.14486800000304356, - 0.13150799999857554, - 0.11893899999995483, - 0.1171360000007553, - 0.14773199999763165, - 0.12983499999972992 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.1466710000022431, - 0.17328100000304403, - 0.1293149999983143, - 0.1080530000035651, - 0.10702100000344217, - 0.20902399999613408, - 0.18371599999954924 - ], - "baselineSelfB": [ - 0.13409200000023702, - 0.11385100000188686, - 0.1254289999997127, - 0.11034600000130013, - 0.10189399999944726, - 0.11756600000080653, - 0.14673100000072736 - ] - }, - "historicalReference": { - "A": 8, - "U": 6, - "C": 21, - "plateSwitches": 7, - "commands": [ - [ - 1, - 4 - ], - [ - 0, - -3 - ], - [ - 3, - 5 - ], - [ - 5, - -1 - ], - [ - 4, - -2 - ], - [ - 2, - -1 - ], - [ - 1, - 2 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "mercenary-near-grim-chest", - "title": "Mercenary's chest (by Grim)", - "optimumA": 8, - "solvers": { - "originalBfs": { - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - [ - 2, - 3 - ], - [ - 1, - 3 - ], - [ - 3, - -6 - ], - [ - 5, - 6 - ], - [ - 0, - -2 - ], - [ - 3, - -2 - ], - [ - 4, - 2 - ], - [ - 5, - 1 - ] - ], - "samplesMs": [ - 103.6997210000045, - 115.46138799999608, - 107.06817600000068, - 102.32011000000057, - 100.22756999999547, - 100.69973200000095, - 111.03184100000362 - ], - "timingMs": { - "min": 100.22756999999547, - "median": 103.6997210000045, - "p95": 115.46138799999608, - "max": 115.46138799999608 - } - }, - "upstream": { - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - [ - 3, - -6 - ], - [ - 5, - 6 - ], - [ - 4, - 2 - ], - [ - 3, - -2 - ], - [ - 5, - 1 - ], - [ - 2, - 3 - ], - [ - 1, - 3 - ], - [ - 0, - -2 - ] - ], - "samplesMs": [ - 30.50765699999465, - 29.63531299999886, - 34.25368400000298, - 31.108320999999705, - 32.98959199999808, - 35.38451599999826, - 35.12606199999573 - ], - "timingMs": { - "min": 29.63531299999886, - "median": 32.98959199999808, - "p95": 35.38451599999826, - "max": 35.38451599999826 - } - }, - "candidate": { - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - [ - 2, - 3 - ], - [ - 1, - 3 - ], - [ - 3, - -6 - ], - [ - 5, - 6 - ], - [ - 0, - -2 - ], - [ - 3, - -2 - ], - [ - 4, - 2 - ], - [ - 5, - 1 - ] - ], - "samplesMs": [ - 0.09011499999905936, - 0.10735199999908218, - 0.09140800000022864, - 0.12449699999706354, - 0.08769199999369448, - 0.12648099999933038, - 0.0907459999943967 - ], - "timingMs": { - "min": 0.08769199999369448, - "median": 0.09140800000022864, - "p95": 0.12648099999933038, - "max": 0.12648099999933038 - } - }, - "baseline": { - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - [ - 2, - 3 - ], - [ - 1, - 3 - ], - [ - 3, - -6 - ], - [ - 5, - 6 - ], - [ - 0, - -2 - ], - [ - 3, - -2 - ], - [ - 4, - 2 - ], - [ - 5, - 1 - ] - ], - "samplesMs": [ - 0.09585399999923538, - 0.07507299999997485, - 0.07295999999769265, - 0.09478300000046147, - 0.0757240000020829, - 0.07189800000196556, - 0.070596999998088 - ], - "timingMs": { - "min": 0.070596999998088, - "median": 0.07507299999997485, - "p95": 0.09585399999923538, - "max": 0.09585399999923538 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.2852516481076364, - "noiseRatio": 1.4294240145276385, - "lowerRatio": 1.0514720451417177, - "upperRatio": 1.5710087648985054 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.2852516481076364, - "noiseRatio": 1.4294240145276385, - "lowerRatio": 1.0514720451417177, - "upperRatio": 1.5710087648985054, - "candidate": [ - 0.09011499999905936, - 0.10735199999908218, - 0.09140800000022864, - 0.12449699999706354, - 0.08769199999369448, - 0.12648099999933038, - 0.0907459999943967 - ], - "baseline": [ - 0.09585399999923538, - 0.07507299999997485, - 0.07295999999769265, - 0.09478300000046147, - 0.0757240000020829, - 0.07189800000196556, - 0.070596999998088 - ], - "selfA": [ - 0.059769999999844, - 0.08783199999743374, - 0.04704100000526523, - 0.03536399999575224, - 0.035734000004595146, - 0.03413100000034319, - 0.031778000004123896 - ], - "selfB": [ - 0.043356000001949724, - 0.05503300000418676, - 0.03664500000013504, - 0.03427100000408245, - 0.03306000000156928, - 0.034831999997550156, - 0.03167799999937415 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.1095127548243418, - "noiseRatio": 1.3047741061979186, - "lowerRatio": 0.8619348051404087, - "upperRatio": 1.4282037873123912, - "candidate": [ - 0.07118700000137324, - 0.07404200000019046, - 0.07116800000221701, - 0.06659100000251783, - 0.07129799999529496, - 0.07397199999832083, - 0.06968400000187103 - ], - "baseline": [ - 0.06824200000119163, - 0.053640999998606276, - 0.06182299999636598, - 0.09897899999486981, - 0.06235400000150548, - 0.05517300000065006, - 0.05755700000008801 - ], - "selfA": [ - 0.06560899999749381, - 0.05266999999730615, - 0.0841870000003837, - 0.06650000000081491, - 0.06203300000197487, - 0.05512299999827519, - 0.058307999999669846 - ], - "selfB": [ - 0.05631500000163214, - 0.08580000000074506, - 0.0657089999949676, - 0.05546400000457652, - 0.057826999996905215, - 0.061923000001115724, - 0.06225300000369316 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.059769999999844, - 0.08783199999743374, - 0.04704100000526523, - 0.03536399999575224, - 0.035734000004595146, - 0.03413100000034319, - 0.031778000004123896 - ], - "baselineSelfB": [ - 0.043356000001949724, - 0.05503300000418676, - 0.03664500000013504, - 0.03427100000408245, - 0.03306000000156928, - 0.034831999997550156, - 0.03167799999937415 - ] - }, - "historicalReference": { - "A": 8, - "U": 6, - "C": 25, - "plateSwitches": 7, - "commands": [ - [ - 3, - -6 - ], - [ - 5, - 6 - ], - [ - 4, - 2 - ], - [ - 3, - -2 - ], - [ - 5, - 1 - ], - [ - 2, - 3 - ], - [ - 1, - 3 - ], - [ - 0, - -2 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "popular-lock", - "title": "Popular lock", - "optimumA": 13, - "solvers": { - "originalBfs": { - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - [ - 2, - -1 - ], - [ - 3, - -1 - ], - [ - 1, - -2 - ], - [ - 4, - -6 - ], - [ - 0, - 4 - ], - [ - 4, - -4 - ], - [ - 3, - -4 - ], - [ - 0, - 2 - ], - [ - 1, - -4 - ], - [ - 4, - -6 - ], - [ - 0, - 6 - ], - [ - 2, - -3 - ], - [ - 4, - -3 - ] - ], - "samplesMs": [ - 12.193998000002466, - 10.646194000000833, - 10.419943000000785, - 10.032790000004752, - 10.938302999995358, - 10.174182999995537, - 10.439902999998594 - ], - "timingMs": { - "min": 10.032790000004752, - "median": 10.439902999998594, - "p95": 12.193998000002466, - "max": 12.193998000002466 - } - }, - "upstream": { - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - [ - 4, - -4 - ], - [ - 3, - -2 - ], - [ - 2, - -1 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - 6 - ], - [ - 4, - -6 - ], - [ - 3, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - 3 - ], - [ - 4, - -6 - ], - [ - 2, - -3 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 6.413843000002089, - 5.8275809999977355, - 6.043526000001293, - 5.798356999999669, - 7.191613999995752, - 6.0573460000014165, - 6.129375999997137 - ], - "timingMs": { - "min": 5.798356999999669, - "median": 6.0573460000014165, - "p95": 7.191613999995752, - "max": 7.191613999995752 - } - }, - "candidate": { - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - [ - 4, - -4 - ], - [ - 2, - -1 - ], - [ - 3, - -2 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - 6 - ], - [ - 2, - -3 - ], - [ - 4, - -6 - ], - [ - 1, - -3 - ], - [ - 0, - 3 - ], - [ - 3, - -3 - ], - [ - 4, - -6 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 0.5802240000048187, - 0.5621759999994538, - 0.560563999999431, - 0.6886860000013257, - 0.7211650000026566, - 0.5734129999982542, - 0.5543850000030943 - ], - "timingMs": { - "min": 0.5543850000030943, - "median": 0.5734129999982542, - "p95": 0.7211650000026566, - "max": 0.7211650000026566 - } - }, - "baseline": { - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - [ - 4, - -4 - ], - [ - 2, - -1 - ], - [ - 3, - -2 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - 6 - ], - [ - 2, - -3 - ], - [ - 4, - -6 - ], - [ - 1, - -3 - ], - [ - 0, - 3 - ], - [ - 3, - -3 - ], - [ - 4, - -6 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 0.5761469999997644, - 0.5817449999958626, - 0.6215450000017881, - 0.5673040000037872, - 0.6296880000008969, - 0.6063819999981206, - 0.5995719999991707 - ], - "timingMs": { - "min": 0.5673040000037872, - "median": 0.5995719999991707, - "p95": 0.6296880000008969, - "max": 0.6296880000008969 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 1.0093062195973268, - "noiseRatio": 1.2108212795985236, - "lowerRatio": 0.896512263533293, - "upperRatio": 1.136291254849094 - }, - "timingAttempts": [ - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 1.0093062195973268, - "noiseRatio": 1.2108212795985236, - "lowerRatio": 0.896512263533293, - "upperRatio": 1.136291254849094, - "candidate": [ - 0.5802240000048187, - 0.5621759999994538, - 0.560563999999431, - 0.6886860000013257, - 0.7211650000026566, - 0.5734129999982542, - 0.5543850000030943 - ], - "baseline": [ - 0.5761469999997644, - 0.5817449999958626, - 0.6215450000017881, - 0.5673040000037872, - 0.6296880000008969, - 0.6063819999981206, - 0.5995719999991707 - ], - "selfA": [ - 0.49143000000185566, - 0.4836280000017723, - 0.4858010000025388, - 0.5723110000035376, - 0.7714409999971394, - 0.6432290000011562, - 0.5160260000047856 - ], - "selfB": [ - 0.5159870000061346, - 0.5590310000043246, - 0.4518499999976484, - 0.47976200000266545, - 0.8175200000041514, - 0.5127710000015213, - 0.45999299999675713 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.49143000000185566, - 0.4836280000017723, - 0.4858010000025388, - 0.5723110000035376, - 0.7714409999971394, - 0.6432290000011562, - 0.5160260000047856 - ], - "baselineSelfB": [ - 0.5159870000061346, - 0.5590310000043246, - 0.4518499999976484, - 0.47976200000266545, - 0.8175200000041514, - 0.5127710000015213, - 0.45999299999675713 - ] - }, - "historicalReference": { - "A": 13, - "U": 5, - "C": 46, - "plateSwitches": 12, - "commands": [ - [ - 4, - -4 - ], - [ - 3, - -2 - ], - [ - 2, - -1 - ], - [ - 1, - -3 - ], - [ - 4, - -3 - ], - [ - 0, - 6 - ], - [ - 4, - -6 - ], - [ - 3, - -3 - ], - [ - 1, - -3 - ], - [ - 0, - 3 - ], - [ - 4, - -6 - ], - [ - 2, - -3 - ], - [ - 0, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "alberto-mine-hut-chest", - "title": "Chest in Alberto's mine hut", - "optimumA": 9, - "solvers": { - "originalBfs": { - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - [ - 3, - -4 - ], - [ - 1, - -2 - ], - [ - 4, - 6 - ], - [ - 0, - -3 - ], - [ - 3, - -6 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - 3 - ] - ], - "samplesMs": [ - 15.54966300000524, - 9.159835999998904, - 9.576733000001695, - 9.728412000004028, - 9.481871000003594, - 9.502863000001526, - 9.873650000001362 - ], - "timingMs": { - "min": 9.159835999998904, - "median": 9.576733000001695, - "p95": 15.54966300000524, - "max": 15.54966300000524 - } - }, - "upstream": { - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - [ - 3, - -4 - ], - [ - 4, - 3 - ], - [ - 2, - -3 - ], - [ - 1, - -5 - ], - [ - 3, - -6 - ], - [ - 4, - 6 - ], - [ - 1, - -3 - ], - [ - 3, - -3 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 5.980021000003035, - 5.648862000001827, - 5.611265999999887, - 5.470332999997481, - 5.791577000003599, - 5.7830529999991995, - 5.695751999999629 - ], - "timingMs": { - "min": 5.470332999997481, - "median": 5.695751999999629, - "p95": 5.980021000003035, - "max": 5.980021000003035 - } - }, - "candidate": { - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - [ - 3, - -4 - ], - [ - 1, - -2 - ], - [ - 4, - 6 - ], - [ - 0, - -3 - ], - [ - 3, - -6 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - 3 - ] - ], - "samplesMs": [ - 0.05858799999987241, - 0.07028600000194274, - 0.06553799999528565, - 0.05515299999387935, - 0.05858799999987241, - 0.07267899999715155, - 0.06283499999699416 - ], - "timingMs": { - "min": 0.05515299999387935, - "median": 0.06283499999699416, - "p95": 0.07267899999715155, - "max": 0.07267899999715155 - } - }, - "baseline": { - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - [ - 3, - -4 - ], - [ - 1, - -2 - ], - [ - 4, - 6 - ], - [ - 0, - -3 - ], - [ - 3, - -6 - ], - [ - 1, - -6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ], - [ - 4, - 3 - ] - ], - "samplesMs": [ - 0.04632999999739695, - 0.10008099999686237, - 0.05662499999743886, - 0.045968999998876825, - 0.049414000000979286, - 0.1601809999992838, - 0.08660999999847263 - ], - "timingMs": { - "min": 0.045968999998876825, - "median": 0.05662499999743886, - "p95": 0.1601809999992838, - "max": 0.1601809999992838 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: regression. Conflicting verdicts remain inconclusive.", - "ratio": 0.9008130853445011, - "noiseRatio": 1.7548282742932664, - "lowerRatio": 0.598029839822097, - "upperRatio": 1.3568958615330553 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9008130853445011, - "noiseRatio": 1.7548282742932664, - "lowerRatio": 0.598029839822097, - "upperRatio": 1.3568958615330553, - "candidate": [ - 0.05858799999987241, - 0.07028600000194274, - 0.06553799999528565, - 0.05515299999387935, - 0.05858799999987241, - 0.07267899999715155, - 0.06283499999699416 - ], - "baseline": [ - 0.04632999999739695, - 0.10008099999686237, - 0.05662499999743886, - 0.045968999998876825, - 0.049414000000979286, - 0.1601809999992838, - 0.08660999999847263 - ], - "selfA": [ - 0.02981500000169035, - 0.02666000000317581, - 0.08006100000056904, - 0.022392999999283347, - 0.022013000001606997, - 0.028281999999308027, - 0.02891299999464536 - ], - "selfB": [ - 0.02895400000124937, - 0.028001999999105465, - 0.03077599999960512, - 0.0257290000008652, - 0.02392600000166567, - 0.02279399999679299, - 0.02276500000152737 - ] - }, - { - "verdict": "regression", - "reason": "Matched slowdown exceeds the unchanged-control uncertainty envelope.", - "ratio": 1.2917820899185781, - "noiseRatio": 1.1457017671607903, - "lowerRatio": 1.206108926568913, - "upperRatio": 1.383540848654075, - "candidate": [ - 0.03038599999854341, - 0.02932400000281632, - 0.03126699999847915, - 0.030795999999099877, - 0.028173000006063376, - 0.03356099999655271, - 0.03794700000435114 - ], - "baseline": [ - 0.02370599999994738, - 0.023716000003332738, - 0.02407600000151433, - 0.023725999999442138, - 0.022693999999319203, - 0.02264399999694433, - 0.03107599999930244 - ], - "selfA": [ - 0.022053000000596512, - 0.02208299999620067, - 0.02278400000068359, - 0.022224000000278465, - 0.027581999995163642, - 0.02278400000068359, - 0.031126000001677312 - ], - "selfB": [ - 0.022032999993825797, - 0.021942999999737367, - 0.02170299999852432, - 0.02270400000270456, - 0.0240159999957541, - 0.022022999997716397, - 0.040340999999898486 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.02981500000169035, - 0.02666000000317581, - 0.08006100000056904, - 0.022392999999283347, - 0.022013000001606997, - 0.028281999999308027, - 0.02891299999464536 - ], - "baselineSelfB": [ - 0.02895400000124937, - 0.028001999999105465, - 0.03077599999960512, - 0.0257290000008652, - 0.02392600000166567, - 0.02279399999679299, - 0.02276500000152737 - ] - }, - "historicalReference": { - "A": 9, - "U": 5, - "C": 36, - "plateSwitches": 8, - "commands": [ - [ - 3, - -4 - ], - [ - 4, - 3 - ], - [ - 2, - -3 - ], - [ - 1, - -5 - ], - [ - 3, - -6 - ], - [ - 4, - 6 - ], - [ - 1, - -3 - ], - [ - 3, - -3 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "alberto-mine-upstairs-left-chest", - "title": "Left chest upstairs in Alberto's mine", - "optimumA": 4, - "solvers": { - "originalBfs": { - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 1, - 6 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.039550000001327135, - 0.03953000000183238, - 0.037837000003491994, - 0.03751599999668542, - 0.03972000000067055, - 0.03649500000028638, - 0.04142200000205776 - ], - "timingMs": { - "min": 0.03649500000028638, - "median": 0.03953000000183238, - "p95": 0.04142200000205776, - "max": 0.04142200000205776 - } - }, - "upstream": { - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 1, - 6 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.21720700000150828, - 0.4312690000006114, - 0.21534400000382448, - 0.21983100000215927, - 0.20572000000538537, - 0.2105370000062976, - 0.2195099999953527 - ], - "timingMs": { - "min": 0.20572000000538537, - "median": 0.21720700000150828, - "p95": 0.4312690000006114, - "max": 0.4312690000006114 - } - }, - "candidate": { - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 1, - 6 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.0247079999971902, - 0.026470000004337635, - 0.02085100000113016, - 0.016284000004816335, - 0.021302000001014676, - 0.10232399999949848, - 0.018338000001676846 - ], - "timingMs": { - "min": 0.016284000004816335, - "median": 0.021302000001014676, - "p95": 0.10232399999949848, - "max": 0.10232399999949848 - } - }, - "baseline": { - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 1, - 6 - ], - [ - 3, - 3 - ] - ], - "samplesMs": [ - 0.020020999996631872, - 0.022412999998778105, - 0.016223999999056105, - 0.013990999999805354, - 0.018427000002702698, - 0.019458999995549675, - 0.015052000002469867 - ], - "timingMs": { - "min": 0.013990999999805354, - "median": 0.018427000002702698, - "p95": 0.022412999998778105, - "max": 0.022412999998778105 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.487925133999896, - "noiseRatio": 1.3979035144116803, - "lowerRatio": 0.8286198448781842, - "upperRatio": 2.6718177437737785 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.487925133999896, - "noiseRatio": 1.3979035144116803, - "lowerRatio": 0.8286198448781842, - "upperRatio": 2.6718177437737785, - "candidate": [ - 0.0247079999971902, - 0.026470000004337635, - 0.02085100000113016, - 0.016284000004816335, - 0.021302000001014676, - 0.10232399999949848, - 0.018338000001676846 - ], - "baseline": [ - 0.020020999996631872, - 0.022412999998778105, - 0.016223999999056105, - 0.013990999999805354, - 0.018427000002702698, - 0.019458999995549675, - 0.015052000002469867 - ], - "selfA": [ - 0.012769999993906822, - 0.011608000000705943, - 0.011457000000518747, - 0.02313500000309432, - 0.013951000000815839, - 0.01151699999900302, - 0.012879000001703389 - ], - "selfB": [ - 0.011626999999862164, - 0.011717000001226552, - 0.011488000003737397, - 0.013580999999248888, - 0.011837999998533633, - 0.011187000003701542, - 0.011016999997082166 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9358178677041551, - "noiseRatio": 1.2650722642512822, - "lowerRatio": 0.48306613142654853, - "upperRatio": 1.8129092986257356, - "candidate": [ - 0.014691999997012317, - 0.020320000003266614, - 0.01504199999908451, - 0.013961000004201196, - 0.013971000000310596, - 0.013741000002482906, - 0.01717599999392405 - ], - "baseline": [ - 0.012067999996361323, - 0.0885340000022552, - 0.012088000003132038, - 0.013689999999769498, - 0.012018000001262408, - 0.013019999998505227, - 0.011888000000908505 - ], - "selfA": [ - 0.011346999999659602, - 0.01651500000298256, - 0.012629000004380941, - 0.011418000001867767, - 0.011446999997133389, - 0.011446999997133389, - 0.016194000003451947 - ], - "selfB": [ - 0.011647999999695458, - 0.012398999999277294, - 0.011316999996779487, - 0.01151699999900302, - 0.011336999996274244, - 0.011758000000554603, - 0.012287999998079613 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.012769999993906822, - 0.011608000000705943, - 0.011457000000518747, - 0.02313500000309432, - 0.013951000000815839, - 0.01151699999900302, - 0.012879000001703389 - ], - "baselineSelfB": [ - 0.011626999999862164, - 0.011717000001226552, - 0.011488000003737397, - 0.013580999999248888, - 0.011837999998533633, - 0.011187000003701542, - 0.011016999997082166 - ] - }, - "historicalReference": { - "A": 4, - "U": 3, - "C": 20, - "plateSwitches": 3, - "commands": [ - [ - 1, - 5 - ], - [ - 2, - 6 - ], - [ - 1, - 6 - ], - [ - 3, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "alberto-mine-upstairs-right-chest", - "title": "Right chest upstairs in Alberto's mine", - "optimumA": 5, - "solvers": { - "originalBfs": { - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - [ - 0, - 3 - ], - [ - 1, - 2 - ], - [ - 2, - 2 - ], - [ - 4, - 4 - ], - [ - 3, - 2 - ] - ], - "samplesMs": [ - 3.2137870000005933, - 2.1705670000010286, - 2.239350000003469, - 2.7121420000039507, - 2.7879759999996168, - 3.270541999998386, - 2.3086849999963306 - ], - "timingMs": { - "min": 2.1705670000010286, - "median": 2.7121420000039507, - "p95": 3.270541999998386, - "max": 3.270541999998386 - } - }, - "upstream": { - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - [ - 0, - 3 - ], - [ - 2, - 2 - ], - [ - 1, - 2 - ], - [ - 4, - 4 - ], - [ - 3, - 2 - ] - ], - "samplesMs": [ - 1.6186859999943408, - 1.7235040000014124, - 1.7385770000037155, - 1.7366640000036568, - 1.9132389999940642, - 1.6731179999987944, - 1.650394000003871 - ], - "timingMs": { - "min": 1.6186859999943408, - "median": 1.7235040000014124, - "p95": 1.9132389999940642, - "max": 1.9132389999940642 - } - }, - "candidate": { - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - [ - 0, - 3 - ], - [ - 1, - 2 - ], - [ - 2, - 2 - ], - [ - 4, - 4 - ], - [ - 3, - 2 - ] - ], - "samplesMs": [ - 0.03578399999969406, - 0.029415000004519243, - 0.02674999999726424, - 0.03757700000278419, - 0.03501300000061747, - 0.2792909999989206, - 0.0316469999961555 - ], - "timingMs": { - "min": 0.02674999999726424, - "median": 0.03501300000061747, - "p95": 0.2792909999989206, - "max": 0.2792909999989206 - } - }, - "baseline": { - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - [ - 0, - 3 - ], - [ - 1, - 2 - ], - [ - 2, - 2 - ], - [ - 4, - 4 - ], - [ - 3, - 2 - ] - ], - "samplesMs": [ - 0.02636000000347849, - 0.023385000000416767, - 0.023885999995400198, - 0.045328000000154134, - 0.03139799999917159, - 0.027211000000534113, - 0.025418000004719943 - ], - "timingMs": { - "min": 0.023385000000416767, - "median": 0.02636000000347849, - "p95": 0.045328000000154134, - "max": 0.045328000000154134 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.5610623841521194, - "noiseRatio": 1.1108149875916935, - "lowerRatio": 0.6429848726381551, - "upperRatio": 3.790004821133784 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.5610623841521194, - "noiseRatio": 1.1108149875916935, - "lowerRatio": 0.6429848726381551, - "upperRatio": 3.790004821133784, - "candidate": [ - 0.03578399999969406, - 0.029415000004519243, - 0.02674999999726424, - 0.03757700000278419, - 0.03501300000061747, - 0.2792909999989206, - 0.0316469999961555 - ], - "baseline": [ - 0.02636000000347849, - 0.023385000000416767, - 0.023885999995400198, - 0.045328000000154134, - 0.03139799999917159, - 0.027211000000534113, - 0.025418000004719943 - ], - "selfA": [ - 0.017515999999886844, - 0.013208999997004867, - 0.012889999998151325, - 0.013480000001436565, - 0.012798999996448401, - 0.013169999998353887, - 0.012979999999515712 - ], - "selfB": [ - 0.014332000006106682, - 0.01344000000244705, - 0.01300999999511987, - 0.013399999996181577, - 0.013559999999415595, - 0.014682000000902917, - 0.012808999999833759 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.276538956617125, - "noiseRatio": 1.1962433562633301, - "lowerRatio": 1.02174388626455, - "upperRatio": 1.5948729712674932, - "candidate": [ - 0.020389999997860286, - 0.02134200000000419, - 0.01748599999700673, - 0.016804999999294523, - 0.01840799999627052, - 0.017376000003423542, - 0.01685600000200793 - ], - "baseline": [ - 0.013691000000108033, - 0.013359999997192062, - 0.013740999995206948, - 0.012949999996635597, - 0.014220999997633044, - 0.013139999995473772, - 0.020521000005828682 - ], - "selfA": [ - 0.013801000000967178, - 0.019308999995701015, - 0.013579999998910353, - 0.012778999996953644, - 0.012710000002698507, - 0.01292000000103144, - 0.014241999997466337 - ], - "selfB": [ - 0.013429999999061693, - 0.014272000000346452, - 0.012859000002208631, - 0.012949000003573019, - 0.012960000000020955, - 0.013980999996419996, - 0.01314999999885913 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.017515999999886844, - 0.013208999997004867, - 0.012889999998151325, - 0.013480000001436565, - 0.012798999996448401, - 0.013169999998353887, - 0.012979999999515712 - ], - "baselineSelfB": [ - 0.014332000006106682, - 0.01344000000244705, - 0.01300999999511987, - 0.013399999996181577, - 0.013559999999415595, - 0.014682000000902917, - 0.012808999999833759 - ] - }, - "historicalReference": { - "A": 5, - "U": 5, - "C": 13, - "plateSwitches": 4, - "commands": [ - [ - 0, - 3 - ], - [ - 2, - 2 - ], - [ - 1, - 2 - ], - [ - 4, - 4 - ], - [ - 3, - 2 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "cavalorn-storeroom-door", - "title": "Hunter Cavalorn's storeroom door", - "optimumA": 11, - "solvers": { - "originalBfs": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 0, - -1 - ], - [ - 3, - -2 - ], - [ - 1, - -6 - ], - [ - 5, - 4 - ], - [ - 0, - -4 - ], - [ - 3, - -2 - ], - [ - 1, - -6 - ], - [ - 4, - -4 - ], - [ - 5, - 6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ] - ], - "samplesMs": [ - 89.01093799999944, - 80.96997499999998, - 82.67584199999692, - 85.84810600000492, - 87.5740650000007, - 83.22516899999755, - 86.76756100000057 - ], - "timingMs": { - "min": 80.96997499999998, - "median": 85.84810600000492, - "p95": 89.01093799999944, - "max": 89.01093799999944 - } - }, - "upstream": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 3, - -1 - ], - [ - 4, - -1 - ], - [ - 5, - 4 - ], - [ - 2, - -3 - ], - [ - 0, - -2 - ], - [ - 1, - -6 - ], - [ - 3, - -6 - ], - [ - 5, - 6 - ], - [ - 4, - -3 - ], - [ - 1, - -6 - ], - [ - 0, - -3 - ] - ], - "samplesMs": [ - 41.1922160000031, - 42.65492799999629, - 43.757456999999704, - 43.41924799999833, - 42.717342000003555, - 43.488172999997914, - 47.82670200000575 - ], - "timingMs": { - "min": 41.1922160000031, - "median": 43.41924799999833, - "p95": 47.82670200000575, - "max": 47.82670200000575 - } - }, - "candidate": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 3, - -2 - ], - [ - 5, - 4 - ], - [ - 0, - -1 - ], - [ - 1, - -6 - ], - [ - 0, - -4 - ], - [ - 3, - -2 - ], - [ - 1, - -6 - ], - [ - 4, - -4 - ], - [ - 5, - 6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ] - ], - "samplesMs": [ - 0.2305969999943045, - 0.21958100000483682, - 0.21743699999933597, - 0.23307999999815365, - 0.2544329999946058, - 0.21845899999607354, - 0.21779699999751756 - ], - "timingMs": { - "min": 0.21743699999933597, - "median": 0.21958100000483682, - "p95": 0.2544329999946058, - "max": 0.2544329999946058 - } - }, - "baseline": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 3, - -2 - ], - [ - 5, - 4 - ], - [ - 0, - -1 - ], - [ - 1, - -6 - ], - [ - 0, - -4 - ], - [ - 3, - -2 - ], - [ - 1, - -6 - ], - [ - 4, - -4 - ], - [ - 5, - 6 - ], - [ - 2, - -3 - ], - [ - 3, - -3 - ] - ], - "samplesMs": [ - 0.21152799999981653, - 0.2097959999955492, - 0.22449799999594688, - 0.21069799999531824, - 0.20967599999858066, - 0.21013599999423604, - 0.2737919999999576 - ], - "timingMs": { - "min": 0.20967599999858066, - "median": 0.21069799999531824, - "p95": 0.2737919999999576, - "max": 0.2737919999999576 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 1.0296320088791049, - "noiseRatio": 1.3819885810604369, - "lowerRatio": 0.8955646084687054, - "upperRatio": 1.1837695054979016 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0296320088791049, - "noiseRatio": 1.3819885810604369, - "lowerRatio": 0.8955646084687054, - "upperRatio": 1.1837695054979016, - "candidate": [ - 0.2305969999943045, - 0.21958100000483682, - 0.21743699999933597, - 0.23307999999815365, - 0.2544329999946058, - 0.21845899999607354, - 0.21779699999751756 - ], - "baseline": [ - 0.21152799999981653, - 0.2097959999955492, - 0.22449799999594688, - 0.21069799999531824, - 0.20967599999858066, - 0.21013599999423604, - 0.2737919999999576 - ], - "selfA": [ - 0.28309599999920465, - 0.12362600000051316, - 0.11836800000310177, - 0.13813700000173412, - 0.12466699999640696, - 0.1234859999967739, - 0.15292000000044936 - ], - "selfB": [ - 0.1598909999956959, - 0.13718599999992875, - 0.12060100000235252, - 0.15960099999938393, - 0.11916899999778252, - 0.11750700000266079, - 0.1355840000032913 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.1309262222565448, - "noiseRatio": 1.2598060574634062, - "lowerRatio": 0.9366055575541085, - "upperRatio": 1.3655632404397424, - "candidate": [ - 0.16683199999533826, - 0.18952600000193343, - 0.15767699999560136, - 0.1923700000043027, - 0.17130799999722512, - 0.18368599999666912, - 0.145368999998027 - ], - "baseline": [ - 0.16363600000477163, - 0.14726200000586687, - 0.15273899999738205, - 0.16516799999953946, - 0.17851800000062212, - 0.11621500000183005, - 0.14701100000092993 - ], - "selfA": [ - 0.15100699999311473, - 0.13251899999886518, - 0.15529399999650195, - 0.1612119999990682, - 0.1371759999965434, - 0.11929999999847496, - 0.1316679999945336 - ], - "selfB": [ - 0.12969499999599066, - 0.14862300000095274, - 0.13358100000186823, - 0.23630600000615232, - 0.12157300000399118, - 0.1295350000000326, - 0.13777700000355253 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.28309599999920465, - 0.12362600000051316, - 0.11836800000310177, - 0.13813700000173412, - 0.12466699999640696, - 0.1234859999967739, - 0.15292000000044936 - ], - "baselineSelfB": [ - 0.1598909999956959, - 0.13718599999992875, - 0.12060100000235252, - 0.15960099999938393, - 0.11916899999778252, - 0.11750700000266079, - 0.1355840000032913 - ] - }, - "historicalReference": { - "A": 11, - "U": 6, - "C": 41, - "plateSwitches": 10, - "commands": [ - [ - 3, - -1 - ], - [ - 4, - -1 - ], - [ - 5, - 4 - ], - [ - 2, - -3 - ], - [ - 0, - -2 - ], - [ - 1, - -6 - ], - [ - 3, - -6 - ], - [ - 5, - 6 - ], - [ - 4, - -3 - ], - [ - 1, - -6 - ], - [ - 0, - -3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - }, - { - "id": "old-to-new-camp-bandits-chest", - "title": "Chest by bandits on the path to the New Camp", - "optimumA": 10, - "solvers": { - "originalBfs": { - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - [ - 2, - -1 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 0, - 3 - ], - [ - 2, - -3 - ], - [ - 3, - 3 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ], - [ - 5, - -2 - ] - ], - "samplesMs": [ - 67.6412249999994, - 69.8904790000015, - 75.50874500000646, - 71.07624399999622, - 67.5849290000042, - 70.11189300000115, - 69.81533599999966 - ], - "timingMs": { - "min": 67.5849290000042, - "median": 69.8904790000015, - "p95": 75.50874500000646, - "max": 75.50874500000646 - } - }, - "upstream": { - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - [ - 5, - -2 - ], - [ - 4, - 6 - ], - [ - 2, - -1 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 0, - 3 - ], - [ - 4, - 6 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 27.31730399999651, - 27.873411000000488, - 27.836896000000706, - 28.83828299999732, - 30.98008700000355, - 27.19507000000158, - 29.070592999996734 - ], - "timingMs": { - "min": 27.19507000000158, - "median": 27.873411000000488, - "p95": 30.98008700000355, - "max": 30.98008700000355 - } - }, - "candidate": { - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - [ - 4, - 6 - ], - [ - 5, - -2 - ], - [ - 2, - -1 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 0.47354300000006333, - 0.3922909999964759, - 0.39261099999566795, - 0.5669229999984964, - 0.42150499999843305, - 0.47142900000471855, - 0.39204000000609085 - ], - "timingMs": { - "min": 0.39204000000609085, - "median": 0.42150499999843305, - "p95": 0.5669229999984964, - "max": 0.5669229999984964 - } - }, - "baseline": { - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - [ - 4, - 6 - ], - [ - 5, - -2 - ], - [ - 2, - -1 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 4, - 6 - ], - [ - 0, - 3 - ] - ], - "samplesMs": [ - 0.42110399999364745, - 0.40192499999830034, - 0.4081649999934598, - 0.431099000001268, - 0.5397919999959413, - 0.5167270000019926, - 0.41836999999941327 - ], - "timingMs": { - "min": 0.40192499999830034, - "median": 0.42110399999364745, - "p95": 0.5397919999959413, - "max": 0.5397919999959413 - } - } - }, - "quality": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "timing": { - "verdict": "inconclusive", - "reason": "First: inconclusive; bounded repeat: inconclusive. Conflicting verdicts remain inconclusive.", - "ratio": 0.9892063971650459, - "noiseRatio": 1.087938982953031, - "lowerRatio": 0.8317200041755886, - "upperRatio": 1.1765128784682548 - }, - "timingAttempts": [ - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 0.9892063971650459, - "noiseRatio": 1.087938982953031, - "lowerRatio": 0.8317200041755886, - "upperRatio": 1.1765128784682548, - "candidate": [ - 0.47354300000006333, - 0.3922909999964759, - 0.39261099999566795, - 0.5669229999984964, - 0.42150499999843305, - 0.47142900000471855, - 0.39204000000609085 - ], - "baseline": [ - 0.42110399999364745, - 0.40192499999830034, - 0.4081649999934598, - 0.431099000001268, - 0.5397919999959413, - 0.5167270000019926, - 0.41836999999941327 - ], - "selfA": [ - 0.32217499999387655, - 0.30742299999837996, - 0.3121800000008079, - 0.2949040000021341, - 0.2665310000011232, - 0.2485240000023623, - 0.30919499999436084 - ], - "selfB": [ - 0.3258999999961816, - 0.30911500000365777, - 0.2913780000017141, - 0.27406199999677483, - 0.25461299999733455, - 0.2568170000013197, - 0.28195399999822257 - ] - }, - { - "verdict": "inconclusive", - "reason": "Measurement overlaps the control envelope or unchanged-control noise exceeds 25%; no performance pass is established.", - "ratio": 1.0073851085495418, - "noiseRatio": 1.5435618049946231, - "lowerRatio": 0.6733100683786263, - "upperRatio": 1.5072175578350326, - "candidate": [ - 0.6927930000019842, - 0.35863899999822024, - 0.34312599999975646, - 0.35830900000291876, - 0.36398799999733455, - 0.37846999999601394, - 0.41390300000057323 - ], - "baseline": [ - 0.36374800000339746, - 0.40292699999554316, - 0.6399129999990691, - 0.3501070000056643, - 0.4282939999975497, - 0.32939600000099745, - 0.35711799999990035 - ], - "selfA": [ - 0.3425150000039139, - 0.30326599999534665, - 0.34249499999714317, - 0.32473900000331923, - 0.3266619999994873, - 0.3286949999965145, - 0.35013700000126846 - ], - "selfB": [ - 0.3587100000004284, - 0.3275930000017979, - 0.3348939999996219, - 0.36730300000635907, - 0.4327919999996084, - 0.27856900000188034, - 0.6953160000048229 - ] - } - ], - "calibration": { - "baselineSelfA": [ - 0.32217499999387655, - 0.30742299999837996, - 0.3121800000008079, - 0.2949040000021341, - 0.2665310000011232, - 0.2485240000023623, - 0.30919499999436084 - ], - "baselineSelfB": [ - 0.3258999999961816, - 0.30911500000365777, - 0.2913780000017141, - 0.27406199999677483, - 0.25461299999733455, - 0.2568170000013197, - 0.28195399999822257 - ] - }, - "historicalReference": { - "A": 10, - "U": 5, - "C": 33, - "plateSwitches": 9, - "commands": [ - [ - 5, - -2 - ], - [ - 4, - 6 - ], - [ - 2, - -1 - ], - [ - 0, - 3 - ], - [ - 3, - 3 - ], - [ - 0, - 3 - ], - [ - 4, - 6 - ], - [ - 3, - 3 - ], - [ - 2, - -3 - ], - [ - 0, - 3 - ] - ], - "timingCompared": false - }, - "differences": { - "originalBfs": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "upstream": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - }, - "baseline": { - "quality": "passed", - "deltas": { - "A": 0, - "U": 0, - "C": 0, - "plateSwitches": 0 - }, - "regressions": [] - } - } - } - ], - "metadata": { - "environment": { - "node": "v24.19.0", - "v8": "13.6.233.17-node.51", - "platform": "linux", - "arch": "x64", - "cpu": "AMD EPYC 9V74 80-Core Processor", - "logicalCpus": 9, - "osRelease": "6.18.35", - "totalMemoryBytes": 23109910528, - "execArgv": [] - }, - "source": { - "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", - "dirty": false, - "files": { - "errors.ts": "830e0995bb1d5b98e8ce0495a22e5ae169cab10b2bdff2612e58323c397306f5", - "index.ts": "a1a2eedf5010cd66fc5d451035923e6ab417b36ba84cb2da85789cea17a1569a", - "indexed-heap.ts": "f776755f7405968b567922473c1e6ea5a63dae7643c4bd62b993159e900b7fbc", - "indexed.ts": "279b11d625a033f45723dd94df13a9be9fdfe267cff2535d5470b2011f11e125", - "lock-model.ts": "b4674921412e2acfbf0eaeff2031b3fc3907dffe886c42e588cd5ba6348ec966", - "matrix-analysis.ts": "a105dee650d645a730e3cc7e62a8e9faae31b29ad98c28aafce5ef15df236fc6", - "matrix-search.ts": "380ae745289a7ffd1997a6b9aa7dc3dd06a56e17453732d41042891c42435d3a", - "search-bfs.ts": "011c675973ccc8d753c0b9b3608882b16ca93ef4b460635c3474d8d5fc705feb", - "search-limits.ts": "91afc0940d49af46d9bb57d91af45df992f0684593d2f2d8bc3c13fad3f4c681", - "types.ts": "a2e72cf02e31694c52c1621cf5359863585b8383aa83a7d4e977c8547e8c879b" - }, - "harness": { - "catalog.ts": "2b75c9e9b5fe6997006307bf74cd90d8645d6d61fc527681f209c164680b880a", - "cli.ts": "b717c3d69c13d4a5b9a77589b1465f96338fe9d8be6501249f4470db035efe33", - "comparison.ts": "680ac76d266b51ff1f951fd3e232248bc5df0ab9eb70123285d31d176fb3a709", - "harness.ts": "14d23d697b2c4dd8adc13beac84b1cd3c0236aa34ae42ea96935fb30ac75e9f8", - "memory.ts": "429b45f541a2ac97e46fb6598ac30966cab4e2dca7e355c56f3ad3e037f43d93", - "rss.ts": "acc321bc34deac44819e5ff6d9af89e91c7394d9d897f7d69ce0cfedfc2e992c", - "solver.bench.ts": "985fbba3b387109261abeb1fdcbecd91b0ccfaa4dde5e0f162ad1758d529647f", - "validation.ts": "c44fc2a3de4da26ab207186a97a6b80ab20a910d27ae335da6cec536e402d77d" - }, - "scripts": { - "benchmark-compare.ts": "d4243c84a0a007b611e04e9c07031c3a7e48fb1a610366349084f0a7ab61cd71", - "benchmark-memory-worker.ts": "807707ca177f43c5ef08b88aa1068ad5f08f391b74f7f8d358159a2e5bf8a49b", - "benchmark-memory.ts": "c25a0407122f1c16261d8f8f9fd6f60cba463a7e288579ba67fde398db8d854f", - "benchmark-snapshot.ts": "50b31ffeac808f5b21f0fb864d2efd8a7c758491cf4e2d611bbe900bf3f9ec32", - "benchmark.ts": "fdb3f22dfdd495a6dde6a27fc3b0db68c7ceb75b94acaff131d30a25b6a06f24" - } - }, - "candidate": { - "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", - "moduleSha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b", - "format": "tuple", - "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/dist/gothic-lock-solver.mjs" - }, - "baseline": { - "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", - "moduleSha256": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "format": "legacy", - "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/reference-matrix/src/index.mjs", - "moduleTree": { - "cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", - "index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", - "matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", - "matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", - "result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", - "search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", - "solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", - "state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" - }, - "snapshotSha256": "c21b6319523f625439403c0417d267b5a63c35abafebef758bfc6c21217a3b94", - "snapshot": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/snapshots/reference-matrix.json", - "kind": "reference-baseline" - }, - "fixtures": { - "count": 45, - "catalogSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", - "manifestSha256": "a634d5328fd8d41470397b622ae0bd32b8aac0355dfc31975e023a63a62e65c9" - }, - "originalBfs": { - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "a87e739a22ccc4215d6d9b14fa06b0738171cb60", - "license": "Apache-2.0", - "files": { - "src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", - "src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", - "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", - "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", - "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" - }, - "notes": "Unmodified source snapshots; imports remain relative within this directory. No upstream UnlockMyLoot solver code is included." - }, - "matrixReference": { - "schemaVersion": 1, - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", - "license": "Apache-2.0", - "files": { - "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", - "src/cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", - "src/index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "src/lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", - "src/matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", - "src/matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", - "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "src/search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", - "src/search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", - "src/solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", - "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" - }, - "notes": "Byte-identical Apache matrix reference, copied from source at the pinned revision. Benchmark-only; excluded from runtime and npm package. No external AGPL solver source." - }, - "upstream": { - "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "solver": { - "path": "index.html", - "sha256": "691f744099a9169f30e9883fddc5fdbb4949d306f3011e601678419d7b93821b", - "blockSha256": "d5bc85f40fd7c5b61097403df399ecb7dfd2b58924dc7a670aa0d05306017380", - "startMarker": "/* solver-start", - "endMarker": "/* solver-end */" - }, - "measured": true, - "externalCheckout": "/workspace/scratch/5d94b8672fa4/unlockmyloot", - "license": "External AGPL source is hash-verified and executed only from the supplied pinned checkout; no upstream solver code is vendored." - }, - "artifacts": { - "gothic-lock-solver.mjs": { - "bytes": 20143, - "sha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b" - }, - "gothic-lock-solver.min.mjs": { - "bytes": 9098, - "sha256": "9a7c0d365a0e2630372200f4c8a7914024b04afd048ca77e6a6783c120676d1f" - }, - "gothic-lock-solver.js": { - "bytes": 20916, - "sha256": "14841c4bc1563f4fd8beae3e277798878a2862efa0840e50b1aef9d5e5dff101" - }, - "gothic-lock-solver.min.js": { - "bytes": 9197, - "sha256": "2a936f190ae38f458e2b209681beb099974f32d654eb33be7249be6ede847694" - }, - "gothic-lock-solver.cli.mjs": { - "bytes": 23199, - "sha256": "75129dff219af243b05ffcbdb63ac1a14a32fd0d0b5ae8ee4528bb4d24b75f81" - } - } - }, - "memory": { - "metric": "Linux /proc/self/status VmHWM × 1024 bytes (current process-image RSS high-water)", - "repetitions": 5, - "sources": [ - "https://docs.kernel.org/filesystems/proc.html", - "https://nodejs.org/api/process.html#processresourceusage" - ], - "accuracy": "Kernel RSS accounting is asynchronous and approximate; this is not an exact allocation count. Raw getrusage maxRSS and startup/current-image peaks are preserved to expose pre-exec launch floors.", - "scope": "Each per-lock observation is a fresh process. Repeated comparison is peak RSS of the selected 45-lock workload per fresh process.", - "includes": "Node runtime, TypeScript benchmark loader, module loading, fixture loading, normalization, independent replay and solver allocations. Startup and loaded current RSS are recorded separately; subtraction is not exact algorithm allocation.", - "browser": { - "available": false, - "reason": "No portable exact browser peak-memory API; Node RSS is not browser memory." - }, - "performance": { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.962246756993597, - "noiseRatio": 1.0949647700001646, - "lowerRatio": 0.8908822461111241, - "upperRatio": 1.0393279531458977 - }, - "attempts": [ - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.962246756993597, - "noiseRatio": 1.0949647700001646, - "lowerRatio": 0.8908822461111241, - "upperRatio": 1.0393279531458977, - "samples": { - "candidate": [ - { - "peakRssBytes": 94666752, - "startupRssBytes": 64344064, - "loadedRssBytes": 65785856, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65351680, - "loadedPeakRssBytes": 66846720, - "resourceUsageMaxRssBytes": 93732864, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94412800, - "startupRssBytes": 65175552, - "loadedRssBytes": 66617344, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66248704, - "loadedPeakRssBytes": 67477504, - "resourceUsageMaxRssBytes": 93741056, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94752768, - "startupRssBytes": 65814528, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66715648, - "loadedPeakRssBytes": 67567616, - "resourceUsageMaxRssBytes": 94523392, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89186304, - "startupRssBytes": 65310720, - "loadedRssBytes": 66883584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66576384, - "loadedPeakRssBytes": 67739648, - "resourceUsageMaxRssBytes": 89006080, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84447232, - "startupRssBytes": 65482752, - "loadedRssBytes": 67186688, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66744320, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "baseline": [ - { - "peakRssBytes": 95948800, - "startupRssBytes": 64970752, - "loadedRssBytes": 67461120, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66179072, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 95100928, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89391104, - "startupRssBytes": 64778240, - "loadedRssBytes": 67137536, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65925120, - "loadedPeakRssBytes": 68210688, - "resourceUsageMaxRssBytes": 88641536, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 100007936, - "startupRssBytes": 64532480, - "loadedRssBytes": 67678208, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65564672, - "loadedPeakRssBytes": 68284416, - "resourceUsageMaxRssBytes": 100007936, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95895552, - "startupRssBytes": 65687552, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67055616, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 95895552, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 93995008, - "startupRssBytes": 65671168, - "loadedRssBytes": 67375104, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66916352, - "loadedPeakRssBytes": 68136960, - "resourceUsageMaxRssBytes": 93188096, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "originalBfs": [ - { - "peakRssBytes": 121143296, - "startupRssBytes": 65421312, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66490368, - "loadedPeakRssBytes": 67485696, - "resourceUsageMaxRssBytes": 120414208, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120922112, - "startupRssBytes": 65372160, - "loadedRssBytes": 67207168, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 120160256, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120438784, - "startupRssBytes": 64557056, - "loadedRssBytes": 66392064, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65339392, - "loadedPeakRssBytes": 67448832, - "resourceUsageMaxRssBytes": 119697408, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120311808, - "startupRssBytes": 65306624, - "loadedRssBytes": 67141632, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66830336, - "loadedPeakRssBytes": 68071424, - "resourceUsageMaxRssBytes": 119595008, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 121356288, - "startupRssBytes": 65691648, - "loadedRssBytes": 67395584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 68116480, - "resourceUsageMaxRssBytes": 120606720, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "upstream": [ - { - "peakRssBytes": 270356480, - "startupRssBytes": 65941504, - "loadedRssBytes": 68431872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67108864, - "loadedPeakRssBytes": 69251072, - "resourceUsageMaxRssBytes": 269815808, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270110720, - "startupRssBytes": 65540096, - "loadedRssBytes": 68685824, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 69443584, - "resourceUsageMaxRssBytes": 269246464, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 268992512, - "startupRssBytes": 64909312, - "loadedRssBytes": 68710400, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65966080, - "loadedPeakRssBytes": 69349376, - "resourceUsageMaxRssBytes": 267882496, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 309043200, - "startupRssBytes": 65306624, - "loadedRssBytes": 68452352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66768896, - "loadedPeakRssBytes": 69234688, - "resourceUsageMaxRssBytes": 309043200, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270135296, - "startupRssBytes": 65089536, - "loadedRssBytes": 68497408, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65785856, - "loadedPeakRssBytes": 69300224, - "resourceUsageMaxRssBytes": 269443072, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "baselineSelfA": [ - { - "peakRssBytes": 95563776, - "startupRssBytes": 65470464, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66633728, - "loadedPeakRssBytes": 68255744, - "resourceUsageMaxRssBytes": 95563776, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95862784, - "startupRssBytes": 65462272, - "loadedRssBytes": 67297280, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66928640, - "loadedPeakRssBytes": 68186112, - "resourceUsageMaxRssBytes": 95862784, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94965760, - "startupRssBytes": 65818624, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67014656, - "loadedPeakRssBytes": 68362240, - "resourceUsageMaxRssBytes": 94965760, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95129600, - "startupRssBytes": 65208320, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66326528, - "loadedPeakRssBytes": 68149248, - "resourceUsageMaxRssBytes": 94162944, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94371840, - "startupRssBytes": 64974848, - "loadedRssBytes": 66940928, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66097152, - "loadedPeakRssBytes": 67719168, - "resourceUsageMaxRssBytes": 94371840, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "baselineSelfB": [ - { - "peakRssBytes": 96808960, - "startupRssBytes": 64933888, - "loadedRssBytes": 67424256, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66068480, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 96731136, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94896128, - "startupRssBytes": 65703936, - "loadedRssBytes": 67407872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 94896128, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84250624, - "startupRssBytes": 65826816, - "loadedRssBytes": 67530752, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66949120, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 97107968, - "startupRssBytes": 65032192, - "loadedRssBytes": 66998272, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66183168, - "loadedPeakRssBytes": 67727360, - "resourceUsageMaxRssBytes": 97107968, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94167040, - "startupRssBytes": 64937984, - "loadedRssBytes": 67428352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66555904, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 93442048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ] - } - } - ], - "wholeCatalog": { - "candidate": { - "samples": [ - { - "peakRssBytes": 94666752, - "startupRssBytes": 64344064, - "loadedRssBytes": 65785856, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65351680, - "loadedPeakRssBytes": 66846720, - "resourceUsageMaxRssBytes": 93732864, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94412800, - "startupRssBytes": 65175552, - "loadedRssBytes": 66617344, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66248704, - "loadedPeakRssBytes": 67477504, - "resourceUsageMaxRssBytes": 93741056, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94752768, - "startupRssBytes": 65814528, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66715648, - "loadedPeakRssBytes": 67567616, - "resourceUsageMaxRssBytes": 94523392, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89186304, - "startupRssBytes": 65310720, - "loadedRssBytes": 66883584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66576384, - "loadedPeakRssBytes": 67739648, - "resourceUsageMaxRssBytes": 89006080, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84447232, - "startupRssBytes": 65482752, - "loadedRssBytes": 67186688, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66744320, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 84447232, - "median": 94412800, - "p95": 94752768, - "max": 94752768 - } - }, - "baseline": { - "samples": [ - { - "peakRssBytes": 95948800, - "startupRssBytes": 64970752, - "loadedRssBytes": 67461120, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66179072, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 95100928, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89391104, - "startupRssBytes": 64778240, - "loadedRssBytes": 67137536, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65925120, - "loadedPeakRssBytes": 68210688, - "resourceUsageMaxRssBytes": 88641536, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 100007936, - "startupRssBytes": 64532480, - "loadedRssBytes": 67678208, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65564672, - "loadedPeakRssBytes": 68284416, - "resourceUsageMaxRssBytes": 100007936, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95895552, - "startupRssBytes": 65687552, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67055616, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 95895552, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 93995008, - "startupRssBytes": 65671168, - "loadedRssBytes": 67375104, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66916352, - "loadedPeakRssBytes": 68136960, - "resourceUsageMaxRssBytes": 93188096, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 89391104, - "median": 95895552, - "p95": 100007936, - "max": 100007936 - } - }, - "originalBfs": { - "samples": [ - { - "peakRssBytes": 121143296, - "startupRssBytes": 65421312, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66490368, - "loadedPeakRssBytes": 67485696, - "resourceUsageMaxRssBytes": 120414208, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120922112, - "startupRssBytes": 65372160, - "loadedRssBytes": 67207168, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 120160256, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120438784, - "startupRssBytes": 64557056, - "loadedRssBytes": 66392064, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65339392, - "loadedPeakRssBytes": 67448832, - "resourceUsageMaxRssBytes": 119697408, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120311808, - "startupRssBytes": 65306624, - "loadedRssBytes": 67141632, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66830336, - "loadedPeakRssBytes": 68071424, - "resourceUsageMaxRssBytes": 119595008, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 121356288, - "startupRssBytes": 65691648, - "loadedRssBytes": 67395584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 68116480, - "resourceUsageMaxRssBytes": 120606720, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 120311808, - "median": 120922112, - "p95": 121356288, - "max": 121356288 - } - }, - "upstream": { - "samples": [ - { - "peakRssBytes": 270356480, - "startupRssBytes": 65941504, - "loadedRssBytes": 68431872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67108864, - "loadedPeakRssBytes": 69251072, - "resourceUsageMaxRssBytes": 269815808, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270110720, - "startupRssBytes": 65540096, - "loadedRssBytes": 68685824, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 69443584, - "resourceUsageMaxRssBytes": 269246464, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 268992512, - "startupRssBytes": 64909312, - "loadedRssBytes": 68710400, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65966080, - "loadedPeakRssBytes": 69349376, - "resourceUsageMaxRssBytes": 267882496, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 309043200, - "startupRssBytes": 65306624, - "loadedRssBytes": 68452352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66768896, - "loadedPeakRssBytes": 69234688, - "resourceUsageMaxRssBytes": 309043200, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270135296, - "startupRssBytes": 65089536, - "loadedRssBytes": 68497408, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65785856, - "loadedPeakRssBytes": 69300224, - "resourceUsageMaxRssBytes": 269443072, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 268992512, - "median": 270135296, - "p95": 309043200, - "max": 309043200 - } - }, - "baselineSelfA": { - "samples": [ - { - "peakRssBytes": 95563776, - "startupRssBytes": 65470464, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66633728, - "loadedPeakRssBytes": 68255744, - "resourceUsageMaxRssBytes": 95563776, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95862784, - "startupRssBytes": 65462272, - "loadedRssBytes": 67297280, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66928640, - "loadedPeakRssBytes": 68186112, - "resourceUsageMaxRssBytes": 95862784, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94965760, - "startupRssBytes": 65818624, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67014656, - "loadedPeakRssBytes": 68362240, - "resourceUsageMaxRssBytes": 94965760, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95129600, - "startupRssBytes": 65208320, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66326528, - "loadedPeakRssBytes": 68149248, - "resourceUsageMaxRssBytes": 94162944, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94371840, - "startupRssBytes": 64974848, - "loadedRssBytes": 66940928, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66097152, - "loadedPeakRssBytes": 67719168, - "resourceUsageMaxRssBytes": 94371840, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 94371840, - "median": 95129600, - "p95": 95862784, - "max": 95862784 - } - }, - "baselineSelfB": { - "samples": [ - { - "peakRssBytes": 96808960, - "startupRssBytes": 64933888, - "loadedRssBytes": 67424256, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66068480, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 96731136, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94896128, - "startupRssBytes": 65703936, - "loadedRssBytes": 67407872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 94896128, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84250624, - "startupRssBytes": 65826816, - "loadedRssBytes": 67530752, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66949120, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 97107968, - "startupRssBytes": 65032192, - "loadedRssBytes": 66998272, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66183168, - "loadedPeakRssBytes": 67727360, - "resourceUsageMaxRssBytes": 97107968, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94167040, - "startupRssBytes": 64937984, - "loadedRssBytes": 67428352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66555904, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 93442048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 84250624, - "median": 94896128, - "p95": 97107968, - "max": 97107968 - } - } - }, - "perLock": [ - { - "id": "ruined-tower-door", - "solvers": { - "candidate": { - "peakRssBytes": 77537280, - "startupRssBytes": 65085440, - "loadedRssBytes": 66658304, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66162688, - "loadedPeakRssBytes": 67620864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 74416128, - "startupRssBytes": 64368640, - "loadedRssBytes": 67383296, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65441792, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78462976, - "startupRssBytes": 65814528, - "loadedRssBytes": 67125248, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66789376, - "loadedPeakRssBytes": 67813376, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 106233856, - "startupRssBytes": 64303104, - "loadedRssBytes": 68497408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65495040, - "loadedPeakRssBytes": 69402624, - "resourceUsageMaxRssBytes": 105652224, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "scatty-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71573504, - "startupRssBytes": 65675264, - "loadedRssBytes": 67117056, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66748416, - "loadedPeakRssBytes": 67698688, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71282688, - "startupRssBytes": 65290240, - "loadedRssBytes": 67387392, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66768896, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78876672, - "startupRssBytes": 66211840, - "loadedRssBytes": 67260416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67403776, - "loadedPeakRssBytes": 68096000, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75640832, - "startupRssBytes": 65265664, - "loadedRssBytes": 68411392, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66207744, - "loadedPeakRssBytes": 69148672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "cor-galom-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72212480, - "startupRssBytes": 65150976, - "loadedRssBytes": 66592768, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66125824, - "loadedPeakRssBytes": 67354624, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71266304, - "startupRssBytes": 65617920, - "loadedRssBytes": 67452928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66957312, - "loadedPeakRssBytes": 68227072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 76562432, - "startupRssBytes": 65687552, - "loadedRssBytes": 67391488, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66940928, - "loadedPeakRssBytes": 68087808, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 73269248, - "startupRssBytes": 65531904, - "loadedRssBytes": 68415488, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67010560, - "loadedPeakRssBytes": 69345280, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "cor-galom-bedroom-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72265728, - "startupRssBytes": 65675264, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67072000, - "loadedPeakRssBytes": 68050944, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71311360, - "startupRssBytes": 65495040, - "loadedRssBytes": 67461120, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 68280320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78819328, - "startupRssBytes": 65101824, - "loadedRssBytes": 67198976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66678784, - "loadedPeakRssBytes": 68079616, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78999552, - "startupRssBytes": 65703936, - "loadedRssBytes": 68456448, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66990080, - "loadedPeakRssBytes": 69292032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "swamp-camp-forge-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72208384, - "startupRssBytes": 65228800, - "loadedRssBytes": 66670592, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66412544, - "loadedPeakRssBytes": 67661824, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71602176, - "startupRssBytes": 64950272, - "loadedRssBytes": 67571712, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65806336, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78815232, - "startupRssBytes": 65097728, - "loadedRssBytes": 67325952, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66359296, - "loadedPeakRssBytes": 68022272, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 83496960, - "startupRssBytes": 65830912, - "loadedRssBytes": 68714496, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67059712, - "loadedPeakRssBytes": 69357568, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "rice-lord-storeroom", - "solvers": { - "candidate": { - "peakRssBytes": 71999488, - "startupRssBytes": 65429504, - "loadedRssBytes": 66740224, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66564096, - "loadedPeakRssBytes": 67616768, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71303168, - "startupRssBytes": 65781760, - "loadedRssBytes": 67747840, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67104768, - "loadedPeakRssBytes": 68378624, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78467072, - "startupRssBytes": 65544192, - "loadedRssBytes": 66985984, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66797568, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75665408, - "startupRssBytes": 65040384, - "loadedRssBytes": 68448256, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65794048, - "loadedPeakRssBytes": 69210112, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-chambers-door", - "solvers": { - "candidate": { - "peakRssBytes": 72138752, - "startupRssBytes": 65363968, - "loadedRssBytes": 66936832, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66924544, - "loadedPeakRssBytes": 67854336, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71692288, - "startupRssBytes": 65499136, - "loadedRssBytes": 67203072, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66719744, - "loadedPeakRssBytes": 67993600, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79171584, - "startupRssBytes": 65282048, - "loadedRssBytes": 67248128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66682880, - "loadedPeakRssBytes": 68141056, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 80289792, - "startupRssBytes": 65380352, - "loadedRssBytes": 68395008, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67026944, - "loadedPeakRssBytes": 69320704, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-throne-hall-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71208960, - "startupRssBytes": 65286144, - "loadedRssBytes": 66727936, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66678784, - "loadedPeakRssBytes": 67715072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71991296, - "startupRssBytes": 65724416, - "loadedRssBytes": 67297280, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67112960, - "loadedPeakRssBytes": 68227072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 72019968, - "startupRssBytes": 65179648, - "loadedRssBytes": 67801088, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66400256, - "loadedPeakRssBytes": 68497408, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 72011776, - "startupRssBytes": 64962560, - "loadedRssBytes": 68632576, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66564096, - "loadedPeakRssBytes": 69357568, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "old-camp-dungeon-door", - "solvers": { - "candidate": { - "peakRssBytes": 77049856, - "startupRssBytes": 65257472, - "loadedRssBytes": 67092480, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66510848, - "loadedPeakRssBytes": 67887104, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 75661312, - "startupRssBytes": 65220608, - "loadedRssBytes": 67448832, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66314240, - "loadedPeakRssBytes": 68214784, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79208448, - "startupRssBytes": 63983616, - "loadedRssBytes": 66473984, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65093632, - "loadedPeakRssBytes": 67276800, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 85356544, - "startupRssBytes": 65171456, - "loadedRssBytes": 68448256, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66473984, - "loadedPeakRssBytes": 69226496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arlin-dungeon-key", - "solvers": { - "candidate": { - "peakRssBytes": 81268736, - "startupRssBytes": 65007616, - "loadedRssBytes": 66449408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66531328, - "loadedPeakRssBytes": 67469312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 79904768, - "startupRssBytes": 65724416, - "loadedRssBytes": 67428352, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66981888, - "loadedPeakRssBytes": 68276224, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 83525632, - "startupRssBytes": 64585728, - "loadedRssBytes": 67338240, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65556480, - "loadedPeakRssBytes": 68038656, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 76693504, - "startupRssBytes": 65495040, - "loadedRssBytes": 68509696, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66912256, - "loadedPeakRssBytes": 69320704, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-wine-cellar", - "solvers": { - "candidate": { - "peakRssBytes": 71606272, - "startupRssBytes": 65417216, - "loadedRssBytes": 66859008, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66609152, - "loadedPeakRssBytes": 67608576, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71225344, - "startupRssBytes": 65245184, - "loadedRssBytes": 67473408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66383872, - "loadedPeakRssBytes": 68194304, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79204352, - "startupRssBytes": 65544192, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66928640, - "loadedPeakRssBytes": 68059136, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75743232, - "startupRssBytes": 65306624, - "loadedRssBytes": 68583424, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 69271552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedside-chest", - "solvers": { - "candidate": { - "peakRssBytes": 76054528, - "startupRssBytes": 65183744, - "loadedRssBytes": 66887680, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66506752, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73519104, - "startupRssBytes": 64593920, - "loadedRssBytes": 67215360, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65310720, - "loadedPeakRssBytes": 67936256, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78987264, - "startupRssBytes": 64577536, - "loadedRssBytes": 67198976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65683456, - "loadedPeakRssBytes": 67956736, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78557184, - "startupRssBytes": 65847296, - "loadedRssBytes": 68599808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67043328, - "loadedPeakRssBytes": 69226496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-wall-chest", - "solvers": { - "candidate": { - "peakRssBytes": 79142912, - "startupRssBytes": 65122304, - "loadedRssBytes": 66957312, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66228224, - "loadedPeakRssBytes": 67846144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 76414976, - "startupRssBytes": 64720896, - "loadedRssBytes": 67080192, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65634304, - "loadedPeakRssBytes": 67944448, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79220736, - "startupRssBytes": 65282048, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66985984, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 111869952, - "startupRssBytes": 64249856, - "loadedRssBytes": 68444160, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65085440, - "loadedPeakRssBytes": 69144576, - "resourceUsageMaxRssBytes": 111263744, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "voran-bedside-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71966720, - "startupRssBytes": 65683456, - "loadedRssBytes": 66994176, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67055616, - "loadedPeakRssBytes": 67956736, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71983104, - "startupRssBytes": 65404928, - "loadedRssBytes": 67502080, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66428928, - "loadedPeakRssBytes": 68198400, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 83914752, - "startupRssBytes": 65208320, - "loadedRssBytes": 67436544, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66514944, - "loadedPeakRssBytes": 68112384, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 123584512, - "startupRssBytes": 65044480, - "loadedRssBytes": 68190208, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66375680, - "loadedPeakRssBytes": 69156864, - "resourceUsageMaxRssBytes": 122744832, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arto-room-door", - "solvers": { - "candidate": { - "peakRssBytes": 71913472, - "startupRssBytes": 65241088, - "loadedRssBytes": 66551808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66400256, - "loadedPeakRssBytes": 67444736, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70885376, - "startupRssBytes": 65437696, - "loadedRssBytes": 67272704, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66719744, - "loadedPeakRssBytes": 67952640, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78823424, - "startupRssBytes": 65675264, - "loadedRssBytes": 67248128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67026944, - "loadedPeakRssBytes": 68091904, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74354688, - "startupRssBytes": 65679360, - "loadedRssBytes": 68038656, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66564096, - "loadedPeakRssBytes": 68825088, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arto-far-bed-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72302592, - "startupRssBytes": 65015808, - "loadedRssBytes": 66326528, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66314240, - "loadedPeakRssBytes": 67268608, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71376896, - "startupRssBytes": 65216512, - "loadedRssBytes": 67575808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66478080, - "loadedPeakRssBytes": 68255744, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78929920, - "startupRssBytes": 65646592, - "loadedRssBytes": 67219456, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66936832, - "loadedPeakRssBytes": 68046848, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74788864, - "startupRssBytes": 65531904, - "loadedRssBytes": 68546560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66867200, - "loadedPeakRssBytes": 69308416, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arto-near-bed-chest", - "solvers": { - "candidate": { - "peakRssBytes": 73064448, - "startupRssBytes": 64270336, - "loadedRssBytes": 65974272, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65445888, - "loadedPeakRssBytes": 66961408, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71794688, - "startupRssBytes": 64516096, - "loadedRssBytes": 66088960, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65302528, - "loadedPeakRssBytes": 66691072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78839808, - "startupRssBytes": 65679360, - "loadedRssBytes": 67383296, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66977792, - "loadedPeakRssBytes": 68071424, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 110370816, - "startupRssBytes": 64880640, - "loadedRssBytes": 68419584, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66088960, - "loadedPeakRssBytes": 69353472, - "resourceUsageMaxRssBytes": 109670400, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedrooms-door", - "solvers": { - "candidate": { - "peakRssBytes": 79302656, - "startupRssBytes": 65355776, - "loadedRssBytes": 66928640, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66699264, - "loadedPeakRssBytes": 67833856, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 78962688, - "startupRssBytes": 65097728, - "loadedRssBytes": 67325952, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66465792, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79081472, - "startupRssBytes": 64839680, - "loadedRssBytes": 67067904, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65900544, - "loadedPeakRssBytes": 67969024, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 84283392, - "startupRssBytes": 64897024, - "loadedRssBytes": 68698112, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65888256, - "loadedPeakRssBytes": 69361664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedrooms-far-chest", - "solvers": { - "candidate": { - "peakRssBytes": 75325440, - "startupRssBytes": 65163264, - "loadedRssBytes": 66867200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66473984, - "loadedPeakRssBytes": 67735552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73637888, - "startupRssBytes": 65191936, - "loadedRssBytes": 67420160, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66863104, - "loadedPeakRssBytes": 68190208, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79294464, - "startupRssBytes": 67203072, - "loadedRssBytes": 67596288, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67887104, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75583488, - "startupRssBytes": 65593344, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66969600, - "loadedPeakRssBytes": 69156864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedrooms-near-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71790592, - "startupRssBytes": 64991232, - "loadedRssBytes": 66301952, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65740800, - "loadedPeakRssBytes": 67002368, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71270400, - "startupRssBytes": 64344064, - "loadedRssBytes": 67489792, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65359872, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79159296, - "startupRssBytes": 64802816, - "loadedRssBytes": 67293184, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66056192, - "loadedPeakRssBytes": 68067328, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 85274624, - "startupRssBytes": 65421312, - "loadedRssBytes": 68435968, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 69005312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-left-chest", - "solvers": { - "candidate": { - "peakRssBytes": 75079680, - "startupRssBytes": 65523712, - "loadedRssBytes": 66965504, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66568192, - "loadedPeakRssBytes": 67747840, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73498624, - "startupRssBytes": 65802240, - "loadedRssBytes": 67244032, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66772992, - "loadedPeakRssBytes": 67981312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79056896, - "startupRssBytes": 64958464, - "loadedRssBytes": 67186688, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65679360, - "loadedPeakRssBytes": 68022272, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 92065792, - "startupRssBytes": 64827392, - "loadedRssBytes": 68497408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66138112, - "loadedPeakRssBytes": 69206016, - "resourceUsageMaxRssBytes": 91205632, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-right-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72089600, - "startupRssBytes": 65638400, - "loadedRssBytes": 67211264, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67026944, - "loadedPeakRssBytes": 67960832, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72290304, - "startupRssBytes": 66674688, - "loadedRssBytes": 67592192, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67465216, - "loadedPeakRssBytes": 68386816, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79151104, - "startupRssBytes": 64765952, - "loadedRssBytes": 67256320, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65757184, - "loadedPeakRssBytes": 68038656, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 76038144, - "startupRssBytes": 65339392, - "loadedRssBytes": 68354048, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66514944, - "loadedPeakRssBytes": 69271552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor2-stairs-chest", - "solvers": { - "candidate": { - "peakRssBytes": 78045184, - "startupRssBytes": 64905216, - "loadedRssBytes": 66740224, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66195456, - "loadedPeakRssBytes": 67358720, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 75186176, - "startupRssBytes": 64901120, - "loadedRssBytes": 67260416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66289664, - "loadedPeakRssBytes": 68206592, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78721024, - "startupRssBytes": 64835584, - "loadedRssBytes": 67063808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66416640, - "loadedPeakRssBytes": 67735552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 93245440, - "startupRssBytes": 65855488, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66990080, - "loadedPeakRssBytes": 69308416, - "resourceUsageMaxRssBytes": 92598272, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor2-chest-2", - "solvers": { - "candidate": { - "peakRssBytes": 72044544, - "startupRssBytes": 65884160, - "loadedRssBytes": 67063808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67031040, - "loadedPeakRssBytes": 67891200, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72298496, - "startupRssBytes": 65290240, - "loadedRssBytes": 67256320, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66572288, - "loadedPeakRssBytes": 68222976, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78888960, - "startupRssBytes": 65081344, - "loadedRssBytes": 67178496, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66191360, - "loadedPeakRssBytes": 68038656, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74493952, - "startupRssBytes": 64372736, - "loadedRssBytes": 68567040, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65331200, - "loadedPeakRssBytes": 69373952, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor3-entrance-chest", - "solvers": { - "candidate": { - "peakRssBytes": 73695232, - "startupRssBytes": 65236992, - "loadedRssBytes": 66547712, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66519040, - "loadedPeakRssBytes": 67457024, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73441280, - "startupRssBytes": 65343488, - "loadedRssBytes": 67702784, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66543616, - "loadedPeakRssBytes": 68182016, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78643200, - "startupRssBytes": 65867776, - "loadedRssBytes": 67047424, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66863104, - "loadedPeakRssBytes": 67829760, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 76640256, - "startupRssBytes": 65904640, - "loadedRssBytes": 68657152, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66973696, - "loadedPeakRssBytes": 69349376, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor3-empty-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71852032, - "startupRssBytes": 66080768, - "loadedRssBytes": 66998272, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67137536, - "loadedPeakRssBytes": 67940352, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71245824, - "startupRssBytes": 65163264, - "loadedRssBytes": 67260416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66428928, - "loadedPeakRssBytes": 68218880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78536704, - "startupRssBytes": 65486848, - "loadedRssBytes": 66928640, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66781184, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75935744, - "startupRssBytes": 64512000, - "loadedRssBytes": 68575232, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65622016, - "loadedPeakRssBytes": 69304320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor4-elixirs-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72155136, - "startupRssBytes": 64491520, - "loadedRssBytes": 66719744, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65703936, - "loadedPeakRssBytes": 67670016, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71299072, - "startupRssBytes": 65552384, - "loadedRssBytes": 67387392, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 82427904, - "startupRssBytes": 65687552, - "loadedRssBytes": 66736128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66732032, - "loadedPeakRssBytes": 67620864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78761984, - "startupRssBytes": 65454080, - "loadedRssBytes": 68730880, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 69373952, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor4-food-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72327168, - "startupRssBytes": 64372736, - "loadedRssBytes": 65421312, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65400832, - "loadedPeakRssBytes": 66535424, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71421952, - "startupRssBytes": 65310720, - "loadedRssBytes": 67407872, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66527232, - "loadedPeakRssBytes": 68280320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79224832, - "startupRssBytes": 65339392, - "loadedRssBytes": 67174400, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66560000, - "loadedPeakRssBytes": 68120576, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 93716480, - "startupRssBytes": 65572864, - "loadedRssBytes": 67801088, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66535424, - "loadedPeakRssBytes": 68571136, - "resourceUsageMaxRssBytes": 93716480, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-top-chest", - "solvers": { - "candidate": { - "peakRssBytes": 79269888, - "startupRssBytes": 66072576, - "loadedRssBytes": 67121152, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67076096, - "loadedPeakRssBytes": 67948544, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 76902400, - "startupRssBytes": 65351680, - "loadedRssBytes": 67710976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66949120, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78548992, - "startupRssBytes": 65146880, - "loadedRssBytes": 66981888, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 67817472, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 86102016, - "startupRssBytes": 65794048, - "loadedRssBytes": 68546560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66965504, - "loadedPeakRssBytes": 69246976, - "resourceUsageMaxRssBytes": 85250048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "fire-lizard-cave-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71999488, - "startupRssBytes": 65421312, - "loadedRssBytes": 67125248, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66793472, - "loadedPeakRssBytes": 67944448, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71417856, - "startupRssBytes": 64843776, - "loadedRssBytes": 67334144, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66064384, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79163392, - "startupRssBytes": 65540096, - "loadedRssBytes": 66850816, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66707456, - "loadedPeakRssBytes": 67694592, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 94773248, - "startupRssBytes": 65036288, - "loadedRssBytes": 68706304, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66277376, - "loadedPeakRssBytes": 69341184, - "resourceUsageMaxRssBytes": 94773248, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "silas-tavern-basement-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72122368, - "startupRssBytes": 64565248, - "loadedRssBytes": 66269184, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65400832, - "loadedPeakRssBytes": 66957312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71405568, - "startupRssBytes": 65630208, - "loadedRssBytes": 67727360, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66318336, - "loadedPeakRssBytes": 68210688, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78974976, - "startupRssBytes": 65765376, - "loadedRssBytes": 67600384, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67031040, - "loadedPeakRssBytes": 68091904, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74752000, - "startupRssBytes": 65855488, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67010560, - "loadedPeakRssBytes": 69316608, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "silas-tavern-storeroom-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71946240, - "startupRssBytes": 66117632, - "loadedRssBytes": 67166208, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67235840, - "loadedPeakRssBytes": 67985408, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71139328, - "startupRssBytes": 65794048, - "loadedRssBytes": 67760128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67022848, - "loadedPeakRssBytes": 68370432, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79196160, - "startupRssBytes": 65155072, - "loadedRssBytes": 67252224, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66383872, - "loadedPeakRssBytes": 68141056, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78635008, - "startupRssBytes": 64790528, - "loadedRssBytes": 68329472, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66011136, - "loadedPeakRssBytes": 69222400, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "silas-tavern-storeroom-door", - "solvers": { - "candidate": { - "peakRssBytes": 72241152, - "startupRssBytes": 64372736, - "loadedRssBytes": 65290240, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65490944, - "loadedPeakRssBytes": 66146304, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71512064, - "startupRssBytes": 64557056, - "loadedRssBytes": 67440640, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65273856, - "loadedPeakRssBytes": 68132864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78893056, - "startupRssBytes": 65040384, - "loadedRssBytes": 67399680, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66592768, - "loadedPeakRssBytes": 68124672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75194368, - "startupRssBytes": 65142784, - "loadedRssBytes": 68419584, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66314240, - "loadedPeakRssBytes": 69242880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "jan-pond-house-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71491584, - "startupRssBytes": 65925120, - "loadedRssBytes": 66842624, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66760704, - "loadedPeakRssBytes": 67538944, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70500352, - "startupRssBytes": 65695744, - "loadedRssBytes": 67137536, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66633728, - "loadedPeakRssBytes": 67809280, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78970880, - "startupRssBytes": 65470464, - "loadedRssBytes": 67436544, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66777088, - "loadedPeakRssBytes": 68116480, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 93093888, - "startupRssBytes": 65413120, - "loadedRssBytes": 68034560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66576384, - "loadedPeakRssBytes": 68816896, - "resourceUsageMaxRssBytes": 91906048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "graham-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72069120, - "startupRssBytes": 64765952, - "loadedRssBytes": 66338816, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65581056, - "loadedPeakRssBytes": 67194880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71299072, - "startupRssBytes": 65204224, - "loadedRssBytes": 67563520, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66428928, - "loadedPeakRssBytes": 68218880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78872576, - "startupRssBytes": 65208320, - "loadedRssBytes": 67305472, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66457600, - "loadedPeakRssBytes": 68055040, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 79687680, - "startupRssBytes": 65462272, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66789376, - "loadedPeakRssBytes": 69304320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gravo-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72118272, - "startupRssBytes": 64983040, - "loadedRssBytes": 66686976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66228224, - "loadedPeakRssBytes": 67579904, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70488064, - "startupRssBytes": 65179648, - "loadedRssBytes": 66883584, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66408448, - "loadedPeakRssBytes": 67633152, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79040512, - "startupRssBytes": 65675264, - "loadedRssBytes": 67117056, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66781184, - "loadedPeakRssBytes": 67846144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75771904, - "startupRssBytes": 65347584, - "loadedRssBytes": 68624384, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66322432, - "loadedPeakRssBytes": 69361664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "diego-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72146944, - "startupRssBytes": 65880064, - "loadedRssBytes": 67190784, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67063808, - "loadedPeakRssBytes": 68005888, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72073216, - "startupRssBytes": 65470464, - "loadedRssBytes": 67567616, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66408448, - "loadedPeakRssBytes": 68186112, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78852096, - "startupRssBytes": 65925120, - "loadedRssBytes": 67366912, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67059712, - "loadedPeakRssBytes": 68120576, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78807040, - "startupRssBytes": 65200128, - "loadedRssBytes": 68345856, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66367488, - "loadedPeakRssBytes": 69296128, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "grim-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72241152, - "startupRssBytes": 65159168, - "loadedRssBytes": 66994176, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66891776, - "loadedPeakRssBytes": 67862528, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71405568, - "startupRssBytes": 65433600, - "loadedRssBytes": 67399680, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66494464, - "loadedPeakRssBytes": 68214784, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78860288, - "startupRssBytes": 65060864, - "loadedRssBytes": 67158016, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66310144, - "loadedPeakRssBytes": 68034560, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75022336, - "startupRssBytes": 65683456, - "loadedRssBytes": 68567040, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 69443584, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "mercenary-near-grim-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71467008, - "startupRssBytes": 65024000, - "loadedRssBytes": 66072576, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66228224, - "loadedPeakRssBytes": 66879488, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71958528, - "startupRssBytes": 65830912, - "loadedRssBytes": 67403776, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66977792, - "loadedPeakRssBytes": 68313088, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78442496, - "startupRssBytes": 65724416, - "loadedRssBytes": 66904064, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66744320, - "loadedPeakRssBytes": 67756032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78852096, - "startupRssBytes": 64790528, - "loadedRssBytes": 68591616, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65409024, - "loadedPeakRssBytes": 69369856, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "popular-lock", - "solvers": { - "candidate": { - "peakRssBytes": 72925184, - "startupRssBytes": 64864256, - "loadedRssBytes": 67223552, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66072576, - "loadedPeakRssBytes": 67854336, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72818688, - "startupRssBytes": 65572864, - "loadedRssBytes": 67670016, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66842624, - "loadedPeakRssBytes": 68358144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78925824, - "startupRssBytes": 64843776, - "loadedRssBytes": 67465216, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66355200, - "loadedPeakRssBytes": 68124672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75907072, - "startupRssBytes": 65937408, - "loadedRssBytes": 67903488, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66772992, - "loadedPeakRssBytes": 68808704, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "alberto-mine-hut-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72032256, - "startupRssBytes": 65372160, - "loadedRssBytes": 66945024, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66519040, - "loadedPeakRssBytes": 67907584, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71299072, - "startupRssBytes": 66199552, - "loadedRssBytes": 67772416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67170304, - "loadedPeakRssBytes": 68464640, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78888960, - "startupRssBytes": 65069056, - "loadedRssBytes": 67166208, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66940928, - "loadedPeakRssBytes": 68124672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75685888, - "startupRssBytes": 65064960, - "loadedRssBytes": 68472832, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66490368, - "loadedPeakRssBytes": 69316608, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "alberto-mine-upstairs-left-chest", - "solvers": { - "candidate": { - "peakRssBytes": 70963200, - "startupRssBytes": 65175552, - "loadedRssBytes": 67010560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66551808, - "loadedPeakRssBytes": 67665920, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71032832, - "startupRssBytes": 65646592, - "loadedRssBytes": 67481600, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68358144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 71507968, - "startupRssBytes": 65769472, - "loadedRssBytes": 67080192, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66801664, - "loadedPeakRssBytes": 67776512, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 72077312, - "startupRssBytes": 65044480, - "loadedRssBytes": 68321280, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66244608, - "loadedPeakRssBytes": 69312512, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "alberto-mine-upstairs-right-chest", - "solvers": { - "candidate": { - "peakRssBytes": 70955008, - "startupRssBytes": 65347584, - "loadedRssBytes": 66658304, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 67461120, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70983680, - "startupRssBytes": 65761280, - "loadedRssBytes": 67334144, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66994176, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78843904, - "startupRssBytes": 65380352, - "loadedRssBytes": 67477504, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66445312, - "loadedPeakRssBytes": 68112384, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 73433088, - "startupRssBytes": 65761280, - "loadedRssBytes": 67727360, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66560000, - "loadedPeakRssBytes": 68489216, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "cavalorn-storeroom-door", - "solvers": { - "candidate": { - "peakRssBytes": 72126464, - "startupRssBytes": 65568768, - "loadedRssBytes": 67010560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66990080, - "loadedPeakRssBytes": 67907584, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71397376, - "startupRssBytes": 65282048, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66547712, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79187968, - "startupRssBytes": 65269760, - "loadedRssBytes": 67366912, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66609152, - "loadedPeakRssBytes": 68096000, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 85778432, - "startupRssBytes": 65150976, - "loadedRssBytes": 68558848, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66166784, - "loadedPeakRssBytes": 69103616, - "resourceUsageMaxRssBytes": 85106688, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "old-to-new-camp-bandits-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72785920, - "startupRssBytes": 65982464, - "loadedRssBytes": 67162112, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67121152, - "loadedPeakRssBytes": 67997696, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71737344, - "startupRssBytes": 65581056, - "loadedRssBytes": 67416064, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66609152, - "loadedPeakRssBytes": 68075520, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79044608, - "startupRssBytes": 65212416, - "loadedRssBytes": 67309568, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66351104, - "loadedPeakRssBytes": 68112384, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74752000, - "startupRssBytes": 65073152, - "loadedRssBytes": 68218880, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66187264, - "loadedPeakRssBytes": 69152768, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - } - ] - } -} diff --git a/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md b/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md deleted file mode 100644 index 2a11b80..0000000 --- a/docs/benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md +++ /dev/null @@ -1,3858 +0,0 @@ -# Gothic Lock Solver benchmark evidence - -Scope: **full-45-catalog**; quality: **passed**; performance: **inconclusive**. - -Warmups: 2; repetitions: 7. A = grouped actions, U = distinct plates, C = unit divisions, S = plate switches. - -Only synchronous public solver call; cloning, legacy/reference normalization, replay and metrics excluded. -Per-lock baseline-vs-itself calibration first; matched rounds rotate by lock and round. A non-pass receives one bounded repeat with all raw attempts retained. -Paired log-ratio mean with conservative 2.776 standard-error envelope; control sets tolerance. >=5 samples; >25% control envelope cannot certify a pass. - -| Implementation | A | U | C | S | Sum of per-lock medians (ms) | -| --- | ---: | ---: | ---: | ---: | ---: | -| originalBfs | 483 | 245 | 1727 | 438 | 2653.701 | -| upstream | 491 | 245 | 1695 | 446 | 1517.403 | -| candidate | 483 | 245 | 1723 | 438 | 47.363 | -| baseline | 483 | 245 | 1723 | 438 | 48.507 | - -| Lock | Optimum A | Implementation | A/U/C/S | Median / p95 ms | Quality vs base | Timing vs base | -| --- | ---: | --- | --- | --- | --- | --- | -| ruined-tower-door | 17 | originalBfs | 17/6/57/16 | 89.658 / 252.696 | | | -| ruined-tower-door | 17 | upstream | 17/6/57/16 | 93.831 / 132.731 | | | -| ruined-tower-door | 17 | candidate | 17/6/57/16 | 3.577 / 4.910 | passed | inconclusive | -| ruined-tower-door | 17 | baseline | 17/6/57/16 | 3.509 / 5.843 | | | -| scatty-chest | 9 | originalBfs | 9/5/31/8 | 12.406 / 33.726 | | | -| scatty-chest | 9 | upstream | 9/5/31/8 | 5.647 / 6.514 | | | -| scatty-chest | 9 | candidate | 9/5/31/8 | 0.137 / 0.165 | passed | inconclusive | -| scatty-chest | 9 | baseline | 9/5/31/8 | 0.138 / 0.198 | | | -| cor-galom-chest | 5 | originalBfs | 5/4/19/4 | 0.506 / 0.552 | | | -| cor-galom-chest | 5 | upstream | 5/4/15/4 | 0.343 / 0.463 | | | -| cor-galom-chest | 5 | candidate | 5/4/15/4 | 0.081 / 0.116 | passed | inconclusive | -| cor-galom-chest | 5 | baseline | 5/4/15/4 | 0.040 / 0.134 | | | -| cor-galom-bedroom-chest | 14 | originalBfs | 14/5/60/13 | 71.330 / 78.037 | | | -| cor-galom-bedroom-chest | 14 | upstream | 14/5/60/13 | 32.020 / 32.880 | | | -| cor-galom-bedroom-chest | 14 | candidate | 14/5/60/13 | 0.238 / 0.327 | passed | inconclusive | -| cor-galom-bedroom-chest | 14 | baseline | 14/5/60/13 | 0.199 / 0.222 | | | -| swamp-camp-forge-chest | 11 | originalBfs | 11/6/41/10 | 106.387 / 120.809 | | | -| swamp-camp-forge-chest | 11 | upstream | 11/6/41/10 | 38.003 / 42.506 | | | -| swamp-camp-forge-chest | 11 | candidate | 11/6/41/10 | 0.241 / 0.461 | passed | inconclusive | -| swamp-camp-forge-chest | 11 | baseline | 11/6/41/10 | 0.212 / 0.247 | | | -| rice-lord-storeroom | 9 | originalBfs | 9/5/30/8 | 8.856 / 9.087 | | | -| rice-lord-storeroom | 9 | upstream | 9/5/30/8 | 4.829 / 5.141 | | | -| rice-lord-storeroom | 9 | candidate | 9/5/30/8 | 0.159 / 0.227 | passed | inconclusive | -| rice-lord-storeroom | 9 | baseline | 9/5/30/8 | 0.124 / 0.127 | | | -| gomez-chambers-door | 9 | originalBfs | 9/6/28/8 | 74.677 / 77.957 | | | -| gomez-chambers-door | 9 | upstream | 9/6/28/8 | 31.106 / 32.987 | | | -| gomez-chambers-door | 9 | candidate | 9/6/28/8 | 0.202 / 0.245 | passed | inconclusive | -| gomez-chambers-door | 9 | baseline | 9/6/28/8 | 0.171 / 0.238 | | | -| gomez-throne-hall-chest | 5 | originalBfs | 5/4/16/4 | 0.179 / 0.220 | | | -| gomez-throne-hall-chest | 5 | upstream | 5/4/16/4 | 0.239 / 0.333 | | | -| gomez-throne-hall-chest | 5 | candidate | 5/4/16/4 | 0.025 / 0.052 | passed | inconclusive | -| gomez-throne-hall-chest | 5 | baseline | 5/4/16/4 | 0.025 / 0.031 | | | -| old-camp-dungeon-door | 17 | originalBfs | 17/6/72/16 | 90.523 / 125.406 | | | -| old-camp-dungeon-door | 17 | upstream | 18/6/68/17 | 42.867 / 49.406 | | | -| old-camp-dungeon-door | 17 | candidate | 17/6/72/16 | 2.244 / 2.823 | passed | inconclusive | -| old-camp-dungeon-door | 17 | baseline | 17/6/72/16 | 2.279 / 2.316 | | | -| arlin-dungeon-key | 7 | originalBfs | 7/5/25/6 | 48.473 / 50.914 | | | -| arlin-dungeon-key | 7 | upstream | 7/5/25/6 | 21.404 / 21.503 | | | -| arlin-dungeon-key | 7 | candidate | 7/5/25/6 | 7.950 / 11.083 | passed | inconclusive | -| arlin-dungeon-key | 7 | baseline | 7/5/25/6 | 8.874 / 11.835 | | | -| gomez-wine-cellar | 9 | originalBfs | 9/6/41/8 | 59.599 / 73.166 | | | -| gomez-wine-cellar | 9 | upstream | 11/6/31/10 | 28.340 / 30.370 | | | -| gomez-wine-cellar | 9 | candidate | 9/6/41/8 | 0.119 / 0.228 | passed | inconclusive | -| gomez-wine-cellar | 9 | baseline | 9/6/41/8 | 0.092 / 0.168 | | | -| gomez-bedside-chest | 23 | originalBfs | 23/5/68/22 | 10.780 / 11.636 | | | -| gomez-bedside-chest | 23 | upstream | 23/5/68/22 | 8.770 / 10.758 | | | -| gomez-bedside-chest | 23 | candidate | 23/5/68/22 | 1.644 / 2.551 | passed | inconclusive | -| gomez-bedside-chest | 23 | baseline | 23/5/68/22 | 1.568 / 2.397 | | | -| gomez-wall-chest | 20 | originalBfs | 20/6/75/19 | 101.362 / 108.772 | | | -| gomez-wall-chest | 20 | upstream | 20/6/75/19 | 87.626 / 104.459 | | | -| gomez-wall-chest | 20 | candidate | 20/6/75/19 | 5.744 / 7.457 | passed | inconclusive | -| gomez-wall-chest | 20 | baseline | 20/6/75/19 | 5.674 / 10.003 | | | -| voran-bedside-chest | 9 | originalBfs | 9/7/38/8 | 199.166 / 217.744 | | | -| voran-bedside-chest | 9 | upstream | 9/7/38/8 | 188.301 / 219.238 | | | -| voran-bedside-chest | 9 | candidate | 9/7/38/8 | 0.112 / 0.184 | passed | inconclusive | -| voran-bedside-chest | 9 | baseline | 9/7/38/8 | 0.129 / 0.531 | | | -| arto-room-door | 7 | originalBfs | 7/5/31/6 | 2.398 / 3.201 | | | -| arto-room-door | 7 | upstream | 7/5/31/6 | 4.815 / 5.955 | | | -| arto-room-door | 7 | candidate | 7/5/31/6 | 0.050 / 0.060 | passed | inconclusive | -| arto-room-door | 7 | baseline | 7/5/31/6 | 0.046 / 0.084 | | | -| arto-far-bed-chest | 11 | originalBfs | 11/5/44/10 | 86.226 / 89.989 | | | -| arto-far-bed-chest | 11 | upstream | 13/5/40/12 | 27.589 / 32.241 | | | -| arto-far-bed-chest | 11 | candidate | 11/5/44/10 | 0.168 / 0.496 | passed | inconclusive | -| arto-far-bed-chest | 11 | baseline | 11/5/44/10 | 0.156 / 0.194 | | | -| arto-near-bed-chest | 13 | originalBfs | 13/6/52/12 | 105.042 / 116.204 | | | -| arto-near-bed-chest | 13 | upstream | 13/6/52/12 | 76.194 / 102.692 | | | -| arto-near-bed-chest | 13 | candidate | 13/6/52/12 | 0.449 / 0.487 | passed | inconclusive | -| arto-near-bed-chest | 13 | baseline | 13/6/52/12 | 0.477 / 0.751 | | | -| gomez-bedrooms-door | 14 | originalBfs | 14/6/45/13 | 104.103 / 110.613 | | | -| gomez-bedrooms-door | 14 | upstream | 14/6/45/13 | 38.404 / 41.819 | | | -| gomez-bedrooms-door | 14 | candidate | 14/6/49/13 | 5.739 / 7.218 | passed | inconclusive | -| gomez-bedrooms-door | 14 | baseline | 14/6/49/13 | 5.763 / 8.625 | | | -| gomez-bedrooms-far-chest | 14 | originalBfs | 14/5/43/13 | 11.208 / 15.988 | | | -| gomez-bedrooms-far-chest | 14 | upstream | 16/5/39/15 | 5.621 / 6.539 | | | -| gomez-bedrooms-far-chest | 14 | candidate | 14/5/43/13 | 1.372 / 2.125 | passed | passed | -| gomez-bedrooms-far-chest | 14 | baseline | 14/5/43/13 | 1.553 / 2.375 | | | -| gomez-bedrooms-near-chest | 12 | originalBfs | 12/6/56/11 | 78.994 / 89.428 | | | -| gomez-bedrooms-near-chest | 12 | upstream | 12/6/56/11 | 43.647 / 49.915 | | | -| gomez-bedrooms-near-chest | 12 | candidate | 12/6/56/11 | 0.099 / 0.104 | passed | inconclusive | -| gomez-bedrooms-near-chest | 12 | baseline | 12/6/56/11 | 0.111 / 0.436 | | | -| raven-tower-left-chest | 11 | originalBfs | 11/6/35/10 | 109.446 / 128.538 | | | -| raven-tower-left-chest | 11 | upstream | 11/6/35/10 | 52.739 / 53.759 | | | -| raven-tower-left-chest | 11 | candidate | 11/6/35/10 | 1.455 / 1.825 | passed | inconclusive | -| raven-tower-left-chest | 11 | baseline | 11/6/35/10 | 1.340 / 1.867 | | | -| raven-tower-right-chest | 8 | originalBfs | 8/6/23/7 | 36.350 / 38.205 | | | -| raven-tower-right-chest | 8 | upstream | 8/6/23/7 | 16.226 / 16.684 | | | -| raven-tower-right-chest | 8 | candidate | 8/6/23/7 | 0.134 / 0.156 | passed | inconclusive | -| raven-tower-right-chest | 8 | baseline | 8/6/23/7 | 0.125 / 0.245 | | | -| raven-tower-floor2-stairs-chest | 16 | originalBfs | 16/6/48/15 | 103.036 / 108.319 | | | -| raven-tower-floor2-stairs-chest | 16 | upstream | 16/6/48/15 | 62.861 / 69.600 | | | -| raven-tower-floor2-stairs-chest | 16 | candidate | 16/6/48/15 | 4.291 / 5.547 | passed | passed | -| raven-tower-floor2-stairs-chest | 16 | baseline | 16/6/48/15 | 4.655 / 5.491 | | | -| raven-tower-floor2-chest-2 | 8 | originalBfs | 8/5/29/7 | 10.942 / 12.065 | | | -| raven-tower-floor2-chest-2 | 8 | upstream | 9/5/27/8 | 3.235 / 3.589 | | | -| raven-tower-floor2-chest-2 | 8 | candidate | 8/5/29/7 | 0.190 / 0.268 | passed | inconclusive | -| raven-tower-floor2-chest-2 | 8 | baseline | 8/5/29/7 | 0.188 / 0.385 | | | -| raven-tower-floor3-entrance-chest | 14 | originalBfs | 14/5/44/13 | 8.888 / 9.141 | | | -| raven-tower-floor3-entrance-chest | 14 | upstream | 14/5/44/13 | 6.582 / 6.925 | | | -| raven-tower-floor3-entrance-chest | 14 | candidate | 14/5/44/13 | 0.606 / 1.505 | passed | inconclusive | -| raven-tower-floor3-entrance-chest | 14 | baseline | 14/5/44/13 | 0.616 / 0.838 | | | -| raven-tower-floor3-empty-chest | 9 | originalBfs | 9/5/41/8 | 7.984 / 8.417 | | | -| raven-tower-floor3-empty-chest | 9 | upstream | 9/5/37/8 | 5.718 / 6.900 | | | -| raven-tower-floor3-empty-chest | 9 | candidate | 9/5/37/8 | 0.077 / 0.103 | passed | inconclusive | -| raven-tower-floor3-empty-chest | 9 | baseline | 9/5/37/8 | 0.076 / 0.151 | | | -| raven-tower-floor4-elixirs-chest | 8 | originalBfs | 8/5/36/7 | 75.479 / 83.178 | | | -| raven-tower-floor4-elixirs-chest | 8 | upstream | 8/5/36/7 | 111.610 / 121.124 | | | -| raven-tower-floor4-elixirs-chest | 8 | candidate | 8/5/36/7 | 0.102 / 0.179 | passed | passed | -| raven-tower-floor4-elixirs-chest | 8 | baseline | 8/5/36/7 | 0.179 / 0.379 | | | -| raven-tower-floor4-food-chest | 11 | originalBfs | 11/6/43/10 | 95.996 / 102.990 | | | -| raven-tower-floor4-food-chest | 11 | upstream | 11/6/43/10 | 57.610 / 61.539 | | | -| raven-tower-floor4-food-chest | 11 | candidate | 11/6/43/10 | 0.253 / 0.272 | passed | inconclusive | -| raven-tower-floor4-food-chest | 11 | baseline | 11/6/43/10 | 0.281 / 0.690 | | | -| raven-tower-top-chest | 20 | originalBfs | 20/6/48/19 | 83.326 / 92.664 | | | -| raven-tower-top-chest | 20 | upstream | 20/6/48/19 | 46.343 / 50.414 | | | -| raven-tower-top-chest | 20 | candidate | 20/6/48/19 | 7.368 / 9.387 | passed | inconclusive | -| raven-tower-top-chest | 20 | baseline | 20/6/48/19 | 7.451 / 7.709 | | | -| fire-lizard-cave-chest | 10 | originalBfs | 10/6/41/9 | 67.629 / 77.178 | | | -| fire-lizard-cave-chest | 10 | upstream | 10/6/41/9 | 56.070 / 58.654 | | | -| fire-lizard-cave-chest | 10 | candidate | 10/6/41/9 | 0.160 / 0.230 | passed | inconclusive | -| fire-lizard-cave-chest | 10 | baseline | 10/6/41/9 | 0.152 / 0.850 | | | -| silas-tavern-basement-chest | 9 | originalBfs | 9/5/30/8 | 6.911 / 7.779 | | | -| silas-tavern-basement-chest | 9 | upstream | 9/5/30/8 | 4.053 / 4.463 | | | -| silas-tavern-basement-chest | 9 | candidate | 9/5/30/8 | 0.100 / 0.233 | passed | inconclusive | -| silas-tavern-basement-chest | 9 | baseline | 9/5/30/8 | 0.089 / 0.106 | | | -| silas-tavern-storeroom-chest | 9 | originalBfs | 9/6/31/8 | 87.225 / 93.561 | | | -| silas-tavern-storeroom-chest | 9 | upstream | 9/6/31/8 | 30.919 / 31.912 | | | -| silas-tavern-storeroom-chest | 9 | candidate | 9/6/31/8 | 0.087 / 0.157 | passed | inconclusive | -| silas-tavern-storeroom-chest | 9 | baseline | 9/6/31/8 | 0.080 / 0.103 | | | -| silas-tavern-storeroom-door | 10 | originalBfs | 10/5/31/9 | 78.692 / 85.264 | | | -| silas-tavern-storeroom-door | 10 | upstream | 10/5/31/9 | 23.705 / 27.696 | | | -| silas-tavern-storeroom-door | 10 | candidate | 10/5/31/9 | 0.158 / 0.179 | passed | passed | -| silas-tavern-storeroom-door | 10 | baseline | 10/5/31/9 | 0.169 / 0.183 | | | -| jan-pond-house-chest | 8 | originalBfs | 8/6/33/7 | 45.814 / 58.215 | | | -| jan-pond-house-chest | 8 | upstream | 8/6/33/7 | 51.663 / 56.488 | | | -| jan-pond-house-chest | 8 | candidate | 8/6/33/7 | 0.085 / 0.092 | passed | inconclusive | -| jan-pond-house-chest | 8 | baseline | 8/6/33/7 | 0.075 / 0.097 | | | -| graham-chest | 10 | originalBfs | 10/6/40/9 | 97.997 / 111.180 | | | -| graham-chest | 10 | upstream | 10/6/40/9 | 31.137 / 32.488 | | | -| graham-chest | 10 | candidate | 10/6/40/9 | 0.091 / 0.093 | passed | inconclusive | -| graham-chest | 10 | baseline | 10/6/40/9 | 0.083 / 0.099 | | | -| gravo-chest | 8 | originalBfs | 8/5/27/7 | 5.361 / 6.206 | | | -| gravo-chest | 8 | upstream | 8/5/27/7 | 5.337 / 5.990 | | | -| gravo-chest | 8 | candidate | 8/5/27/7 | 0.063 / 0.081 | passed | inconclusive | -| gravo-chest | 8 | baseline | 8/5/27/7 | 0.055 / 0.071 | | | -| diego-chest | 11 | originalBfs | 11/6/40/10 | 81.415 / 84.984 | | | -| diego-chest | 11 | upstream | 11/6/40/10 | 31.231 / 47.637 | | | -| diego-chest | 11 | candidate | 11/6/40/10 | 0.147 / 0.180 | passed | passed | -| diego-chest | 11 | baseline | 11/6/40/10 | 0.149 / 0.177 | | | -| grim-chest | 8 | originalBfs | 8/6/21/7 | 107.132 / 123.995 | | | -| grim-chest | 8 | upstream | 8/6/21/7 | 22.792 / 23.272 | | | -| grim-chest | 8 | candidate | 8/6/21/7 | 0.221 / 0.274 | passed | inconclusive | -| grim-chest | 8 | baseline | 8/6/21/7 | 0.196 / 0.289 | | | -| mercenary-near-grim-chest | 8 | originalBfs | 8/6/25/7 | 103.700 / 115.461 | | | -| mercenary-near-grim-chest | 8 | upstream | 8/6/25/7 | 32.990 / 35.385 | | | -| mercenary-near-grim-chest | 8 | candidate | 8/6/25/7 | 0.091 / 0.126 | passed | inconclusive | -| mercenary-near-grim-chest | 8 | baseline | 8/6/25/7 | 0.075 / 0.096 | | | -| popular-lock | 13 | originalBfs | 13/5/46/12 | 10.440 / 12.194 | | | -| popular-lock | 13 | upstream | 13/5/46/12 | 6.057 / 7.192 | | | -| popular-lock | 13 | candidate | 13/5/46/12 | 0.573 / 0.721 | passed | passed | -| popular-lock | 13 | baseline | 13/5/46/12 | 0.600 / 0.630 | | | -| alberto-mine-hut-chest | 9 | originalBfs | 9/5/36/8 | 9.577 / 15.550 | | | -| alberto-mine-hut-chest | 9 | upstream | 9/5/36/8 | 5.696 / 5.980 | | | -| alberto-mine-hut-chest | 9 | candidate | 9/5/36/8 | 0.063 / 0.073 | passed | inconclusive | -| alberto-mine-hut-chest | 9 | baseline | 9/5/36/8 | 0.057 / 0.160 | | | -| alberto-mine-upstairs-left-chest | 4 | originalBfs | 4/3/20/3 | 0.040 / 0.041 | | | -| alberto-mine-upstairs-left-chest | 4 | upstream | 4/3/20/3 | 0.217 / 0.431 | | | -| alberto-mine-upstairs-left-chest | 4 | candidate | 4/3/20/3 | 0.021 / 0.102 | passed | inconclusive | -| alberto-mine-upstairs-left-chest | 4 | baseline | 4/3/20/3 | 0.018 / 0.022 | | | -| alberto-mine-upstairs-right-chest | 5 | originalBfs | 5/5/13/4 | 2.712 / 3.271 | | | -| alberto-mine-upstairs-right-chest | 5 | upstream | 5/5/13/4 | 1.724 / 1.913 | | | -| alberto-mine-upstairs-right-chest | 5 | candidate | 5/5/13/4 | 0.035 / 0.279 | passed | inconclusive | -| alberto-mine-upstairs-right-chest | 5 | baseline | 5/5/13/4 | 0.026 / 0.045 | | | -| cavalorn-storeroom-door | 11 | originalBfs | 11/6/41/10 | 85.848 / 89.011 | | | -| cavalorn-storeroom-door | 11 | upstream | 11/6/41/10 | 43.419 / 47.827 | | | -| cavalorn-storeroom-door | 11 | candidate | 11/6/41/10 | 0.220 / 0.254 | passed | inconclusive | -| cavalorn-storeroom-door | 11 | baseline | 11/6/41/10 | 0.211 / 0.274 | | | -| old-to-new-camp-bandits-chest | 10 | originalBfs | 10/5/33/9 | 69.890 / 75.509 | | | -| old-to-new-camp-bandits-chest | 10 | upstream | 10/5/33/9 | 27.873 / 30.980 | | | -| old-to-new-camp-bandits-chest | 10 | candidate | 10/5/33/9 | 0.422 / 0.567 | passed | inconclusive | -| old-to-new-camp-bandits-chest | 10 | baseline | 10/5/33/9 | 0.421 / 0.540 | | | - -## Per-lock candidate differences - -| Lock | Comparator | ΔA / ΔU / ΔC / ΔS | -| --- | --- | --- | -| ruined-tower-door | originalBfs | 0/0/0/0 | -| ruined-tower-door | upstream | 0/0/0/0 | -| ruined-tower-door | baseline | 0/0/0/0 | -| scatty-chest | originalBfs | 0/0/0/0 | -| scatty-chest | upstream | 0/0/0/0 | -| scatty-chest | baseline | 0/0/0/0 | -| cor-galom-chest | originalBfs | 0/0/-4/0 | -| cor-galom-chest | upstream | 0/0/0/0 | -| cor-galom-chest | baseline | 0/0/0/0 | -| cor-galom-bedroom-chest | originalBfs | 0/0/0/0 | -| cor-galom-bedroom-chest | upstream | 0/0/0/0 | -| cor-galom-bedroom-chest | baseline | 0/0/0/0 | -| swamp-camp-forge-chest | originalBfs | 0/0/0/0 | -| swamp-camp-forge-chest | upstream | 0/0/0/0 | -| swamp-camp-forge-chest | baseline | 0/0/0/0 | -| rice-lord-storeroom | originalBfs | 0/0/0/0 | -| rice-lord-storeroom | upstream | 0/0/0/0 | -| rice-lord-storeroom | baseline | 0/0/0/0 | -| gomez-chambers-door | originalBfs | 0/0/0/0 | -| gomez-chambers-door | upstream | 0/0/0/0 | -| gomez-chambers-door | baseline | 0/0/0/0 | -| gomez-throne-hall-chest | originalBfs | 0/0/0/0 | -| gomez-throne-hall-chest | upstream | 0/0/0/0 | -| gomez-throne-hall-chest | baseline | 0/0/0/0 | -| old-camp-dungeon-door | originalBfs | 0/0/0/0 | -| old-camp-dungeon-door | upstream | -1/0/4/-1 | -| old-camp-dungeon-door | baseline | 0/0/0/0 | -| arlin-dungeon-key | originalBfs | 0/0/0/0 | -| arlin-dungeon-key | upstream | 0/0/0/0 | -| arlin-dungeon-key | baseline | 0/0/0/0 | -| gomez-wine-cellar | originalBfs | 0/0/0/0 | -| gomez-wine-cellar | upstream | -2/0/10/-2 | -| gomez-wine-cellar | baseline | 0/0/0/0 | -| gomez-bedside-chest | originalBfs | 0/0/0/0 | -| gomez-bedside-chest | upstream | 0/0/0/0 | -| gomez-bedside-chest | baseline | 0/0/0/0 | -| gomez-wall-chest | originalBfs | 0/0/0/0 | -| gomez-wall-chest | upstream | 0/0/0/0 | -| gomez-wall-chest | baseline | 0/0/0/0 | -| voran-bedside-chest | originalBfs | 0/0/0/0 | -| voran-bedside-chest | upstream | 0/0/0/0 | -| voran-bedside-chest | baseline | 0/0/0/0 | -| arto-room-door | originalBfs | 0/0/0/0 | -| arto-room-door | upstream | 0/0/0/0 | -| arto-room-door | baseline | 0/0/0/0 | -| arto-far-bed-chest | originalBfs | 0/0/0/0 | -| arto-far-bed-chest | upstream | -2/0/4/-2 | -| arto-far-bed-chest | baseline | 0/0/0/0 | -| arto-near-bed-chest | originalBfs | 0/0/0/0 | -| arto-near-bed-chest | upstream | 0/0/0/0 | -| arto-near-bed-chest | baseline | 0/0/0/0 | -| gomez-bedrooms-door | originalBfs | 0/0/4/0 | -| gomez-bedrooms-door | upstream | 0/0/4/0 | -| gomez-bedrooms-door | baseline | 0/0/0/0 | -| gomez-bedrooms-far-chest | originalBfs | 0/0/0/0 | -| gomez-bedrooms-far-chest | upstream | -2/0/4/-2 | -| gomez-bedrooms-far-chest | baseline | 0/0/0/0 | -| gomez-bedrooms-near-chest | originalBfs | 0/0/0/0 | -| gomez-bedrooms-near-chest | upstream | 0/0/0/0 | -| gomez-bedrooms-near-chest | baseline | 0/0/0/0 | -| raven-tower-left-chest | originalBfs | 0/0/0/0 | -| raven-tower-left-chest | upstream | 0/0/0/0 | -| raven-tower-left-chest | baseline | 0/0/0/0 | -| raven-tower-right-chest | originalBfs | 0/0/0/0 | -| raven-tower-right-chest | upstream | 0/0/0/0 | -| raven-tower-right-chest | baseline | 0/0/0/0 | -| raven-tower-floor2-stairs-chest | originalBfs | 0/0/0/0 | -| raven-tower-floor2-stairs-chest | upstream | 0/0/0/0 | -| raven-tower-floor2-stairs-chest | baseline | 0/0/0/0 | -| raven-tower-floor2-chest-2 | originalBfs | 0/0/0/0 | -| raven-tower-floor2-chest-2 | upstream | -1/0/2/-1 | -| raven-tower-floor2-chest-2 | baseline | 0/0/0/0 | -| raven-tower-floor3-entrance-chest | originalBfs | 0/0/0/0 | -| raven-tower-floor3-entrance-chest | upstream | 0/0/0/0 | -| raven-tower-floor3-entrance-chest | baseline | 0/0/0/0 | -| raven-tower-floor3-empty-chest | originalBfs | 0/0/-4/0 | -| raven-tower-floor3-empty-chest | upstream | 0/0/0/0 | -| raven-tower-floor3-empty-chest | baseline | 0/0/0/0 | -| raven-tower-floor4-elixirs-chest | originalBfs | 0/0/0/0 | -| raven-tower-floor4-elixirs-chest | upstream | 0/0/0/0 | -| raven-tower-floor4-elixirs-chest | baseline | 0/0/0/0 | -| raven-tower-floor4-food-chest | originalBfs | 0/0/0/0 | -| raven-tower-floor4-food-chest | upstream | 0/0/0/0 | -| raven-tower-floor4-food-chest | baseline | 0/0/0/0 | -| raven-tower-top-chest | originalBfs | 0/0/0/0 | -| raven-tower-top-chest | upstream | 0/0/0/0 | -| raven-tower-top-chest | baseline | 0/0/0/0 | -| fire-lizard-cave-chest | originalBfs | 0/0/0/0 | -| fire-lizard-cave-chest | upstream | 0/0/0/0 | -| fire-lizard-cave-chest | baseline | 0/0/0/0 | -| silas-tavern-basement-chest | originalBfs | 0/0/0/0 | -| silas-tavern-basement-chest | upstream | 0/0/0/0 | -| silas-tavern-basement-chest | baseline | 0/0/0/0 | -| silas-tavern-storeroom-chest | originalBfs | 0/0/0/0 | -| silas-tavern-storeroom-chest | upstream | 0/0/0/0 | -| silas-tavern-storeroom-chest | baseline | 0/0/0/0 | -| silas-tavern-storeroom-door | originalBfs | 0/0/0/0 | -| silas-tavern-storeroom-door | upstream | 0/0/0/0 | -| silas-tavern-storeroom-door | baseline | 0/0/0/0 | -| jan-pond-house-chest | originalBfs | 0/0/0/0 | -| jan-pond-house-chest | upstream | 0/0/0/0 | -| jan-pond-house-chest | baseline | 0/0/0/0 | -| graham-chest | originalBfs | 0/0/0/0 | -| graham-chest | upstream | 0/0/0/0 | -| graham-chest | baseline | 0/0/0/0 | -| gravo-chest | originalBfs | 0/0/0/0 | -| gravo-chest | upstream | 0/0/0/0 | -| gravo-chest | baseline | 0/0/0/0 | -| diego-chest | originalBfs | 0/0/0/0 | -| diego-chest | upstream | 0/0/0/0 | -| diego-chest | baseline | 0/0/0/0 | -| grim-chest | originalBfs | 0/0/0/0 | -| grim-chest | upstream | 0/0/0/0 | -| grim-chest | baseline | 0/0/0/0 | -| mercenary-near-grim-chest | originalBfs | 0/0/0/0 | -| mercenary-near-grim-chest | upstream | 0/0/0/0 | -| mercenary-near-grim-chest | baseline | 0/0/0/0 | -| popular-lock | originalBfs | 0/0/0/0 | -| popular-lock | upstream | 0/0/0/0 | -| popular-lock | baseline | 0/0/0/0 | -| alberto-mine-hut-chest | originalBfs | 0/0/0/0 | -| alberto-mine-hut-chest | upstream | 0/0/0/0 | -| alberto-mine-hut-chest | baseline | 0/0/0/0 | -| alberto-mine-upstairs-left-chest | originalBfs | 0/0/0/0 | -| alberto-mine-upstairs-left-chest | upstream | 0/0/0/0 | -| alberto-mine-upstairs-left-chest | baseline | 0/0/0/0 | -| alberto-mine-upstairs-right-chest | originalBfs | 0/0/0/0 | -| alberto-mine-upstairs-right-chest | upstream | 0/0/0/0 | -| alberto-mine-upstairs-right-chest | baseline | 0/0/0/0 | -| cavalorn-storeroom-door | originalBfs | 0/0/0/0 | -| cavalorn-storeroom-door | upstream | 0/0/0/0 | -| cavalorn-storeroom-door | baseline | 0/0/0/0 | -| old-to-new-camp-bandits-chest | originalBfs | 0/0/0/0 | -| old-to-new-camp-bandits-chest | upstream | 0/0/0/0 | -| old-to-new-camp-bandits-chest | baseline | 0/0/0/0 | - -## Isolated process peak RSS - -RSS includes Node, module loading and runtime allocation; it is not exact algorithm heap allocation. Browser peak memory is unavailable. Memory runs are separate from timed calls. - -```json -{ - "metric": "Linux /proc/self/status VmHWM × 1024 bytes (current process-image RSS high-water)", - "repetitions": 5, - "sources": [ - "https://docs.kernel.org/filesystems/proc.html", - "https://nodejs.org/api/process.html#processresourceusage" - ], - "accuracy": "Kernel RSS accounting is asynchronous and approximate; this is not an exact allocation count. Raw getrusage maxRSS and startup/current-image peaks are preserved to expose pre-exec launch floors.", - "scope": "Each per-lock observation is a fresh process. Repeated comparison is peak RSS of the selected 45-lock workload per fresh process.", - "includes": "Node runtime, TypeScript benchmark loader, module loading, fixture loading, normalization, independent replay and solver allocations. Startup and loaded current RSS are recorded separately; subtraction is not exact algorithm allocation.", - "browser": { - "available": false, - "reason": "No portable exact browser peak-memory API; Node RSS is not browser memory." - }, - "performance": { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.962246756993597, - "noiseRatio": 1.0949647700001646, - "lowerRatio": 0.8908822461111241, - "upperRatio": 1.0393279531458977 - }, - "attempts": [ - { - "verdict": "passed", - "reason": "Measured upper slowdown bound lies within the calibrated control envelope.", - "ratio": 0.962246756993597, - "noiseRatio": 1.0949647700001646, - "lowerRatio": 0.8908822461111241, - "upperRatio": 1.0393279531458977, - "samples": { - "candidate": [ - { - "peakRssBytes": 94666752, - "startupRssBytes": 64344064, - "loadedRssBytes": 65785856, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65351680, - "loadedPeakRssBytes": 66846720, - "resourceUsageMaxRssBytes": 93732864, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94412800, - "startupRssBytes": 65175552, - "loadedRssBytes": 66617344, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66248704, - "loadedPeakRssBytes": 67477504, - "resourceUsageMaxRssBytes": 93741056, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94752768, - "startupRssBytes": 65814528, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66715648, - "loadedPeakRssBytes": 67567616, - "resourceUsageMaxRssBytes": 94523392, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89186304, - "startupRssBytes": 65310720, - "loadedRssBytes": 66883584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66576384, - "loadedPeakRssBytes": 67739648, - "resourceUsageMaxRssBytes": 89006080, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84447232, - "startupRssBytes": 65482752, - "loadedRssBytes": 67186688, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66744320, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "baseline": [ - { - "peakRssBytes": 95948800, - "startupRssBytes": 64970752, - "loadedRssBytes": 67461120, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66179072, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 95100928, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89391104, - "startupRssBytes": 64778240, - "loadedRssBytes": 67137536, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65925120, - "loadedPeakRssBytes": 68210688, - "resourceUsageMaxRssBytes": 88641536, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 100007936, - "startupRssBytes": 64532480, - "loadedRssBytes": 67678208, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65564672, - "loadedPeakRssBytes": 68284416, - "resourceUsageMaxRssBytes": 100007936, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95895552, - "startupRssBytes": 65687552, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67055616, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 95895552, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 93995008, - "startupRssBytes": 65671168, - "loadedRssBytes": 67375104, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66916352, - "loadedPeakRssBytes": 68136960, - "resourceUsageMaxRssBytes": 93188096, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "originalBfs": [ - { - "peakRssBytes": 121143296, - "startupRssBytes": 65421312, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66490368, - "loadedPeakRssBytes": 67485696, - "resourceUsageMaxRssBytes": 120414208, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120922112, - "startupRssBytes": 65372160, - "loadedRssBytes": 67207168, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 120160256, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120438784, - "startupRssBytes": 64557056, - "loadedRssBytes": 66392064, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65339392, - "loadedPeakRssBytes": 67448832, - "resourceUsageMaxRssBytes": 119697408, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120311808, - "startupRssBytes": 65306624, - "loadedRssBytes": 67141632, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66830336, - "loadedPeakRssBytes": 68071424, - "resourceUsageMaxRssBytes": 119595008, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 121356288, - "startupRssBytes": 65691648, - "loadedRssBytes": 67395584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 68116480, - "resourceUsageMaxRssBytes": 120606720, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "upstream": [ - { - "peakRssBytes": 270356480, - "startupRssBytes": 65941504, - "loadedRssBytes": 68431872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67108864, - "loadedPeakRssBytes": 69251072, - "resourceUsageMaxRssBytes": 269815808, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270110720, - "startupRssBytes": 65540096, - "loadedRssBytes": 68685824, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 69443584, - "resourceUsageMaxRssBytes": 269246464, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 268992512, - "startupRssBytes": 64909312, - "loadedRssBytes": 68710400, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65966080, - "loadedPeakRssBytes": 69349376, - "resourceUsageMaxRssBytes": 267882496, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 309043200, - "startupRssBytes": 65306624, - "loadedRssBytes": 68452352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66768896, - "loadedPeakRssBytes": 69234688, - "resourceUsageMaxRssBytes": 309043200, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270135296, - "startupRssBytes": 65089536, - "loadedRssBytes": 68497408, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65785856, - "loadedPeakRssBytes": 69300224, - "resourceUsageMaxRssBytes": 269443072, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "baselineSelfA": [ - { - "peakRssBytes": 95563776, - "startupRssBytes": 65470464, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66633728, - "loadedPeakRssBytes": 68255744, - "resourceUsageMaxRssBytes": 95563776, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95862784, - "startupRssBytes": 65462272, - "loadedRssBytes": 67297280, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66928640, - "loadedPeakRssBytes": 68186112, - "resourceUsageMaxRssBytes": 95862784, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94965760, - "startupRssBytes": 65818624, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67014656, - "loadedPeakRssBytes": 68362240, - "resourceUsageMaxRssBytes": 94965760, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95129600, - "startupRssBytes": 65208320, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66326528, - "loadedPeakRssBytes": 68149248, - "resourceUsageMaxRssBytes": 94162944, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94371840, - "startupRssBytes": 64974848, - "loadedRssBytes": 66940928, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66097152, - "loadedPeakRssBytes": 67719168, - "resourceUsageMaxRssBytes": 94371840, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "baselineSelfB": [ - { - "peakRssBytes": 96808960, - "startupRssBytes": 64933888, - "loadedRssBytes": 67424256, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66068480, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 96731136, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94896128, - "startupRssBytes": 65703936, - "loadedRssBytes": 67407872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 94896128, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84250624, - "startupRssBytes": 65826816, - "loadedRssBytes": 67530752, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66949120, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 97107968, - "startupRssBytes": 65032192, - "loadedRssBytes": 66998272, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66183168, - "loadedPeakRssBytes": 67727360, - "resourceUsageMaxRssBytes": 97107968, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94167040, - "startupRssBytes": 64937984, - "loadedRssBytes": 67428352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66555904, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 93442048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ] - } - } - ], - "wholeCatalog": { - "candidate": { - "samples": [ - { - "peakRssBytes": 94666752, - "startupRssBytes": 64344064, - "loadedRssBytes": 65785856, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65351680, - "loadedPeakRssBytes": 66846720, - "resourceUsageMaxRssBytes": 93732864, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94412800, - "startupRssBytes": 65175552, - "loadedRssBytes": 66617344, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66248704, - "loadedPeakRssBytes": 67477504, - "resourceUsageMaxRssBytes": 93741056, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94752768, - "startupRssBytes": 65814528, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66715648, - "loadedPeakRssBytes": 67567616, - "resourceUsageMaxRssBytes": 94523392, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89186304, - "startupRssBytes": 65310720, - "loadedRssBytes": 66883584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66576384, - "loadedPeakRssBytes": 67739648, - "resourceUsageMaxRssBytes": 89006080, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84447232, - "startupRssBytes": 65482752, - "loadedRssBytes": 67186688, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66744320, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 84447232, - "median": 94412800, - "p95": 94752768, - "max": 94752768 - } - }, - "baseline": { - "samples": [ - { - "peakRssBytes": 95948800, - "startupRssBytes": 64970752, - "loadedRssBytes": 67461120, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66179072, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 95100928, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 89391104, - "startupRssBytes": 64778240, - "loadedRssBytes": 67137536, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65925120, - "loadedPeakRssBytes": 68210688, - "resourceUsageMaxRssBytes": 88641536, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 100007936, - "startupRssBytes": 64532480, - "loadedRssBytes": 67678208, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65564672, - "loadedPeakRssBytes": 68284416, - "resourceUsageMaxRssBytes": 100007936, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95895552, - "startupRssBytes": 65687552, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67055616, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 95895552, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 93995008, - "startupRssBytes": 65671168, - "loadedRssBytes": 67375104, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66916352, - "loadedPeakRssBytes": 68136960, - "resourceUsageMaxRssBytes": 93188096, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 89391104, - "median": 95895552, - "p95": 100007936, - "max": 100007936 - } - }, - "originalBfs": { - "samples": [ - { - "peakRssBytes": 121143296, - "startupRssBytes": 65421312, - "loadedRssBytes": 66732032, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66490368, - "loadedPeakRssBytes": 67485696, - "resourceUsageMaxRssBytes": 120414208, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120922112, - "startupRssBytes": 65372160, - "loadedRssBytes": 67207168, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 120160256, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120438784, - "startupRssBytes": 64557056, - "loadedRssBytes": 66392064, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65339392, - "loadedPeakRssBytes": 67448832, - "resourceUsageMaxRssBytes": 119697408, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 120311808, - "startupRssBytes": 65306624, - "loadedRssBytes": 67141632, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66830336, - "loadedPeakRssBytes": 68071424, - "resourceUsageMaxRssBytes": 119595008, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 121356288, - "startupRssBytes": 65691648, - "loadedRssBytes": 67395584, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 68116480, - "resourceUsageMaxRssBytes": 120606720, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 120311808, - "median": 120922112, - "p95": 121356288, - "max": 121356288 - } - }, - "upstream": { - "samples": [ - { - "peakRssBytes": 270356480, - "startupRssBytes": 65941504, - "loadedRssBytes": 68431872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67108864, - "loadedPeakRssBytes": 69251072, - "resourceUsageMaxRssBytes": 269815808, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270110720, - "startupRssBytes": 65540096, - "loadedRssBytes": 68685824, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 69443584, - "resourceUsageMaxRssBytes": 269246464, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 268992512, - "startupRssBytes": 64909312, - "loadedRssBytes": 68710400, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65966080, - "loadedPeakRssBytes": 69349376, - "resourceUsageMaxRssBytes": 267882496, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 309043200, - "startupRssBytes": 65306624, - "loadedRssBytes": 68452352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66768896, - "loadedPeakRssBytes": 69234688, - "resourceUsageMaxRssBytes": 309043200, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 270135296, - "startupRssBytes": 65089536, - "loadedRssBytes": 68497408, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65785856, - "loadedPeakRssBytes": 69300224, - "resourceUsageMaxRssBytes": 269443072, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 268992512, - "median": 270135296, - "p95": 309043200, - "max": 309043200 - } - }, - "baselineSelfA": { - "samples": [ - { - "peakRssBytes": 95563776, - "startupRssBytes": 65470464, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66633728, - "loadedPeakRssBytes": 68255744, - "resourceUsageMaxRssBytes": 95563776, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95862784, - "startupRssBytes": 65462272, - "loadedRssBytes": 67297280, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66928640, - "loadedPeakRssBytes": 68186112, - "resourceUsageMaxRssBytes": 95862784, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94965760, - "startupRssBytes": 65818624, - "loadedRssBytes": 67391488, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67014656, - "loadedPeakRssBytes": 68362240, - "resourceUsageMaxRssBytes": 94965760, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 95129600, - "startupRssBytes": 65208320, - "loadedRssBytes": 67305472, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66326528, - "loadedPeakRssBytes": 68149248, - "resourceUsageMaxRssBytes": 94162944, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94371840, - "startupRssBytes": 64974848, - "loadedRssBytes": 66940928, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66097152, - "loadedPeakRssBytes": 67719168, - "resourceUsageMaxRssBytes": 94371840, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 94371840, - "median": 95129600, - "p95": 95862784, - "max": 95862784 - } - }, - "baselineSelfB": { - "samples": [ - { - "peakRssBytes": 96808960, - "startupRssBytes": 64933888, - "loadedRssBytes": 67424256, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66068480, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 96731136, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94896128, - "startupRssBytes": 65703936, - "loadedRssBytes": 67407872, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 94896128, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 84250624, - "startupRssBytes": 65826816, - "loadedRssBytes": 67530752, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66949120, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 97107968, - "startupRssBytes": 65032192, - "loadedRssBytes": 66998272, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66183168, - "loadedPeakRssBytes": 67727360, - "resourceUsageMaxRssBytes": 97107968, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - { - "peakRssBytes": 94167040, - "startupRssBytes": 64937984, - "loadedRssBytes": 67428352, - "lockCount": 45, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66555904, - "loadedPeakRssBytes": 68247552, - "resourceUsageMaxRssBytes": 93442048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - ], - "peakRssBytes": { - "min": 84250624, - "median": 94896128, - "p95": 97107968, - "max": 97107968 - } - } - }, - "perLock": [ - { - "id": "ruined-tower-door", - "solvers": { - "candidate": { - "peakRssBytes": 77537280, - "startupRssBytes": 65085440, - "loadedRssBytes": 66658304, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66162688, - "loadedPeakRssBytes": 67620864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 74416128, - "startupRssBytes": 64368640, - "loadedRssBytes": 67383296, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65441792, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78462976, - "startupRssBytes": 65814528, - "loadedRssBytes": 67125248, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66789376, - "loadedPeakRssBytes": 67813376, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 106233856, - "startupRssBytes": 64303104, - "loadedRssBytes": 68497408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65495040, - "loadedPeakRssBytes": 69402624, - "resourceUsageMaxRssBytes": 105652224, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "scatty-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71573504, - "startupRssBytes": 65675264, - "loadedRssBytes": 67117056, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66748416, - "loadedPeakRssBytes": 67698688, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71282688, - "startupRssBytes": 65290240, - "loadedRssBytes": 67387392, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66768896, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78876672, - "startupRssBytes": 66211840, - "loadedRssBytes": 67260416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67403776, - "loadedPeakRssBytes": 68096000, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75640832, - "startupRssBytes": 65265664, - "loadedRssBytes": 68411392, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66207744, - "loadedPeakRssBytes": 69148672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "cor-galom-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72212480, - "startupRssBytes": 65150976, - "loadedRssBytes": 66592768, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66125824, - "loadedPeakRssBytes": 67354624, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71266304, - "startupRssBytes": 65617920, - "loadedRssBytes": 67452928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66957312, - "loadedPeakRssBytes": 68227072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 76562432, - "startupRssBytes": 65687552, - "loadedRssBytes": 67391488, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66940928, - "loadedPeakRssBytes": 68087808, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 73269248, - "startupRssBytes": 65531904, - "loadedRssBytes": 68415488, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67010560, - "loadedPeakRssBytes": 69345280, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "cor-galom-bedroom-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72265728, - "startupRssBytes": 65675264, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67072000, - "loadedPeakRssBytes": 68050944, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71311360, - "startupRssBytes": 65495040, - "loadedRssBytes": 67461120, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 68280320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78819328, - "startupRssBytes": 65101824, - "loadedRssBytes": 67198976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66678784, - "loadedPeakRssBytes": 68079616, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78999552, - "startupRssBytes": 65703936, - "loadedRssBytes": 68456448, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66990080, - "loadedPeakRssBytes": 69292032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "swamp-camp-forge-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72208384, - "startupRssBytes": 65228800, - "loadedRssBytes": 66670592, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66412544, - "loadedPeakRssBytes": 67661824, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71602176, - "startupRssBytes": 64950272, - "loadedRssBytes": 67571712, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65806336, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78815232, - "startupRssBytes": 65097728, - "loadedRssBytes": 67325952, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66359296, - "loadedPeakRssBytes": 68022272, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 83496960, - "startupRssBytes": 65830912, - "loadedRssBytes": 68714496, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67059712, - "loadedPeakRssBytes": 69357568, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "rice-lord-storeroom", - "solvers": { - "candidate": { - "peakRssBytes": 71999488, - "startupRssBytes": 65429504, - "loadedRssBytes": 66740224, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66564096, - "loadedPeakRssBytes": 67616768, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71303168, - "startupRssBytes": 65781760, - "loadedRssBytes": 67747840, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67104768, - "loadedPeakRssBytes": 68378624, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78467072, - "startupRssBytes": 65544192, - "loadedRssBytes": 66985984, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66797568, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75665408, - "startupRssBytes": 65040384, - "loadedRssBytes": 68448256, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65794048, - "loadedPeakRssBytes": 69210112, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-chambers-door", - "solvers": { - "candidate": { - "peakRssBytes": 72138752, - "startupRssBytes": 65363968, - "loadedRssBytes": 66936832, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66924544, - "loadedPeakRssBytes": 67854336, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71692288, - "startupRssBytes": 65499136, - "loadedRssBytes": 67203072, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66719744, - "loadedPeakRssBytes": 67993600, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79171584, - "startupRssBytes": 65282048, - "loadedRssBytes": 67248128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66682880, - "loadedPeakRssBytes": 68141056, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 80289792, - "startupRssBytes": 65380352, - "loadedRssBytes": 68395008, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67026944, - "loadedPeakRssBytes": 69320704, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-throne-hall-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71208960, - "startupRssBytes": 65286144, - "loadedRssBytes": 66727936, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66678784, - "loadedPeakRssBytes": 67715072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71991296, - "startupRssBytes": 65724416, - "loadedRssBytes": 67297280, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67112960, - "loadedPeakRssBytes": 68227072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 72019968, - "startupRssBytes": 65179648, - "loadedRssBytes": 67801088, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66400256, - "loadedPeakRssBytes": 68497408, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 72011776, - "startupRssBytes": 64962560, - "loadedRssBytes": 68632576, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66564096, - "loadedPeakRssBytes": 69357568, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "old-camp-dungeon-door", - "solvers": { - "candidate": { - "peakRssBytes": 77049856, - "startupRssBytes": 65257472, - "loadedRssBytes": 67092480, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66510848, - "loadedPeakRssBytes": 67887104, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 75661312, - "startupRssBytes": 65220608, - "loadedRssBytes": 67448832, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66314240, - "loadedPeakRssBytes": 68214784, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79208448, - "startupRssBytes": 63983616, - "loadedRssBytes": 66473984, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65093632, - "loadedPeakRssBytes": 67276800, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 85356544, - "startupRssBytes": 65171456, - "loadedRssBytes": 68448256, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66473984, - "loadedPeakRssBytes": 69226496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arlin-dungeon-key", - "solvers": { - "candidate": { - "peakRssBytes": 81268736, - "startupRssBytes": 65007616, - "loadedRssBytes": 66449408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66531328, - "loadedPeakRssBytes": 67469312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 79904768, - "startupRssBytes": 65724416, - "loadedRssBytes": 67428352, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66981888, - "loadedPeakRssBytes": 68276224, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 83525632, - "startupRssBytes": 64585728, - "loadedRssBytes": 67338240, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65556480, - "loadedPeakRssBytes": 68038656, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 76693504, - "startupRssBytes": 65495040, - "loadedRssBytes": 68509696, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66912256, - "loadedPeakRssBytes": 69320704, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-wine-cellar", - "solvers": { - "candidate": { - "peakRssBytes": 71606272, - "startupRssBytes": 65417216, - "loadedRssBytes": 66859008, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66609152, - "loadedPeakRssBytes": 67608576, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71225344, - "startupRssBytes": 65245184, - "loadedRssBytes": 67473408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66383872, - "loadedPeakRssBytes": 68194304, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79204352, - "startupRssBytes": 65544192, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66928640, - "loadedPeakRssBytes": 68059136, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75743232, - "startupRssBytes": 65306624, - "loadedRssBytes": 68583424, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 69271552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedside-chest", - "solvers": { - "candidate": { - "peakRssBytes": 76054528, - "startupRssBytes": 65183744, - "loadedRssBytes": 66887680, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66506752, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73519104, - "startupRssBytes": 64593920, - "loadedRssBytes": 67215360, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65310720, - "loadedPeakRssBytes": 67936256, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78987264, - "startupRssBytes": 64577536, - "loadedRssBytes": 67198976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65683456, - "loadedPeakRssBytes": 67956736, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78557184, - "startupRssBytes": 65847296, - "loadedRssBytes": 68599808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67043328, - "loadedPeakRssBytes": 69226496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-wall-chest", - "solvers": { - "candidate": { - "peakRssBytes": 79142912, - "startupRssBytes": 65122304, - "loadedRssBytes": 66957312, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66228224, - "loadedPeakRssBytes": 67846144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 76414976, - "startupRssBytes": 64720896, - "loadedRssBytes": 67080192, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65634304, - "loadedPeakRssBytes": 67944448, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79220736, - "startupRssBytes": 65282048, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66985984, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 111869952, - "startupRssBytes": 64249856, - "loadedRssBytes": 68444160, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65085440, - "loadedPeakRssBytes": 69144576, - "resourceUsageMaxRssBytes": 111263744, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "voran-bedside-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71966720, - "startupRssBytes": 65683456, - "loadedRssBytes": 66994176, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67055616, - "loadedPeakRssBytes": 67956736, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71983104, - "startupRssBytes": 65404928, - "loadedRssBytes": 67502080, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66428928, - "loadedPeakRssBytes": 68198400, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 83914752, - "startupRssBytes": 65208320, - "loadedRssBytes": 67436544, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66514944, - "loadedPeakRssBytes": 68112384, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 123584512, - "startupRssBytes": 65044480, - "loadedRssBytes": 68190208, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66375680, - "loadedPeakRssBytes": 69156864, - "resourceUsageMaxRssBytes": 122744832, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arto-room-door", - "solvers": { - "candidate": { - "peakRssBytes": 71913472, - "startupRssBytes": 65241088, - "loadedRssBytes": 66551808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66400256, - "loadedPeakRssBytes": 67444736, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70885376, - "startupRssBytes": 65437696, - "loadedRssBytes": 67272704, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66719744, - "loadedPeakRssBytes": 67952640, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78823424, - "startupRssBytes": 65675264, - "loadedRssBytes": 67248128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67026944, - "loadedPeakRssBytes": 68091904, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74354688, - "startupRssBytes": 65679360, - "loadedRssBytes": 68038656, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66564096, - "loadedPeakRssBytes": 68825088, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arto-far-bed-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72302592, - "startupRssBytes": 65015808, - "loadedRssBytes": 66326528, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66314240, - "loadedPeakRssBytes": 67268608, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71376896, - "startupRssBytes": 65216512, - "loadedRssBytes": 67575808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66478080, - "loadedPeakRssBytes": 68255744, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78929920, - "startupRssBytes": 65646592, - "loadedRssBytes": 67219456, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66936832, - "loadedPeakRssBytes": 68046848, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74788864, - "startupRssBytes": 65531904, - "loadedRssBytes": 68546560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66867200, - "loadedPeakRssBytes": 69308416, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "arto-near-bed-chest", - "solvers": { - "candidate": { - "peakRssBytes": 73064448, - "startupRssBytes": 64270336, - "loadedRssBytes": 65974272, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65445888, - "loadedPeakRssBytes": 66961408, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71794688, - "startupRssBytes": 64516096, - "loadedRssBytes": 66088960, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65302528, - "loadedPeakRssBytes": 66691072, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78839808, - "startupRssBytes": 65679360, - "loadedRssBytes": 67383296, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66977792, - "loadedPeakRssBytes": 68071424, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 110370816, - "startupRssBytes": 64880640, - "loadedRssBytes": 68419584, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66088960, - "loadedPeakRssBytes": 69353472, - "resourceUsageMaxRssBytes": 109670400, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedrooms-door", - "solvers": { - "candidate": { - "peakRssBytes": 79302656, - "startupRssBytes": 65355776, - "loadedRssBytes": 66928640, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66699264, - "loadedPeakRssBytes": 67833856, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 78962688, - "startupRssBytes": 65097728, - "loadedRssBytes": 67325952, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66465792, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79081472, - "startupRssBytes": 64839680, - "loadedRssBytes": 67067904, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65900544, - "loadedPeakRssBytes": 67969024, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 84283392, - "startupRssBytes": 64897024, - "loadedRssBytes": 68698112, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65888256, - "loadedPeakRssBytes": 69361664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedrooms-far-chest", - "solvers": { - "candidate": { - "peakRssBytes": 75325440, - "startupRssBytes": 65163264, - "loadedRssBytes": 66867200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66473984, - "loadedPeakRssBytes": 67735552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73637888, - "startupRssBytes": 65191936, - "loadedRssBytes": 67420160, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66863104, - "loadedPeakRssBytes": 68190208, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79294464, - "startupRssBytes": 67203072, - "loadedRssBytes": 67596288, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67887104, - "loadedPeakRssBytes": 68239360, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75583488, - "startupRssBytes": 65593344, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66969600, - "loadedPeakRssBytes": 69156864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gomez-bedrooms-near-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71790592, - "startupRssBytes": 64991232, - "loadedRssBytes": 66301952, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65740800, - "loadedPeakRssBytes": 67002368, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71270400, - "startupRssBytes": 64344064, - "loadedRssBytes": 67489792, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65359872, - "loadedPeakRssBytes": 68161536, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79159296, - "startupRssBytes": 64802816, - "loadedRssBytes": 67293184, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66056192, - "loadedPeakRssBytes": 68067328, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 85274624, - "startupRssBytes": 65421312, - "loadedRssBytes": 68435968, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 69005312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-left-chest", - "solvers": { - "candidate": { - "peakRssBytes": 75079680, - "startupRssBytes": 65523712, - "loadedRssBytes": 66965504, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66568192, - "loadedPeakRssBytes": 67747840, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73498624, - "startupRssBytes": 65802240, - "loadedRssBytes": 67244032, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66772992, - "loadedPeakRssBytes": 67981312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79056896, - "startupRssBytes": 64958464, - "loadedRssBytes": 67186688, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65679360, - "loadedPeakRssBytes": 68022272, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 92065792, - "startupRssBytes": 64827392, - "loadedRssBytes": 68497408, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66138112, - "loadedPeakRssBytes": 69206016, - "resourceUsageMaxRssBytes": 91205632, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-right-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72089600, - "startupRssBytes": 65638400, - "loadedRssBytes": 67211264, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67026944, - "loadedPeakRssBytes": 67960832, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72290304, - "startupRssBytes": 66674688, - "loadedRssBytes": 67592192, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67465216, - "loadedPeakRssBytes": 68386816, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79151104, - "startupRssBytes": 64765952, - "loadedRssBytes": 67256320, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65757184, - "loadedPeakRssBytes": 68038656, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 76038144, - "startupRssBytes": 65339392, - "loadedRssBytes": 68354048, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66514944, - "loadedPeakRssBytes": 69271552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor2-stairs-chest", - "solvers": { - "candidate": { - "peakRssBytes": 78045184, - "startupRssBytes": 64905216, - "loadedRssBytes": 66740224, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66195456, - "loadedPeakRssBytes": 67358720, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 75186176, - "startupRssBytes": 64901120, - "loadedRssBytes": 67260416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66289664, - "loadedPeakRssBytes": 68206592, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78721024, - "startupRssBytes": 64835584, - "loadedRssBytes": 67063808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66416640, - "loadedPeakRssBytes": 67735552, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 93245440, - "startupRssBytes": 65855488, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66990080, - "loadedPeakRssBytes": 69308416, - "resourceUsageMaxRssBytes": 92598272, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor2-chest-2", - "solvers": { - "candidate": { - "peakRssBytes": 72044544, - "startupRssBytes": 65884160, - "loadedRssBytes": 67063808, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67031040, - "loadedPeakRssBytes": 67891200, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72298496, - "startupRssBytes": 65290240, - "loadedRssBytes": 67256320, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66572288, - "loadedPeakRssBytes": 68222976, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78888960, - "startupRssBytes": 65081344, - "loadedRssBytes": 67178496, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66191360, - "loadedPeakRssBytes": 68038656, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74493952, - "startupRssBytes": 64372736, - "loadedRssBytes": 68567040, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65331200, - "loadedPeakRssBytes": 69373952, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor3-entrance-chest", - "solvers": { - "candidate": { - "peakRssBytes": 73695232, - "startupRssBytes": 65236992, - "loadedRssBytes": 66547712, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66519040, - "loadedPeakRssBytes": 67457024, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 73441280, - "startupRssBytes": 65343488, - "loadedRssBytes": 67702784, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66543616, - "loadedPeakRssBytes": 68182016, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78643200, - "startupRssBytes": 65867776, - "loadedRssBytes": 67047424, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66863104, - "loadedPeakRssBytes": 67829760, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 76640256, - "startupRssBytes": 65904640, - "loadedRssBytes": 68657152, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66973696, - "loadedPeakRssBytes": 69349376, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor3-empty-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71852032, - "startupRssBytes": 66080768, - "loadedRssBytes": 66998272, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67137536, - "loadedPeakRssBytes": 67940352, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71245824, - "startupRssBytes": 65163264, - "loadedRssBytes": 67260416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66428928, - "loadedPeakRssBytes": 68218880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78536704, - "startupRssBytes": 65486848, - "loadedRssBytes": 66928640, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66781184, - "loadedPeakRssBytes": 67825664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75935744, - "startupRssBytes": 64512000, - "loadedRssBytes": 68575232, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65622016, - "loadedPeakRssBytes": 69304320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor4-elixirs-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72155136, - "startupRssBytes": 64491520, - "loadedRssBytes": 66719744, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65703936, - "loadedPeakRssBytes": 67670016, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71299072, - "startupRssBytes": 65552384, - "loadedRssBytes": 67387392, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 82427904, - "startupRssBytes": 65687552, - "loadedRssBytes": 66736128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66732032, - "loadedPeakRssBytes": 67620864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78761984, - "startupRssBytes": 65454080, - "loadedRssBytes": 68730880, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 69373952, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-floor4-food-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72327168, - "startupRssBytes": 64372736, - "loadedRssBytes": 65421312, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65400832, - "loadedPeakRssBytes": 66535424, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71421952, - "startupRssBytes": 65310720, - "loadedRssBytes": 67407872, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66527232, - "loadedPeakRssBytes": 68280320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79224832, - "startupRssBytes": 65339392, - "loadedRssBytes": 67174400, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66560000, - "loadedPeakRssBytes": 68120576, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 93716480, - "startupRssBytes": 65572864, - "loadedRssBytes": 67801088, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66535424, - "loadedPeakRssBytes": 68571136, - "resourceUsageMaxRssBytes": 93716480, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "raven-tower-top-chest", - "solvers": { - "candidate": { - "peakRssBytes": 79269888, - "startupRssBytes": 66072576, - "loadedRssBytes": 67121152, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67076096, - "loadedPeakRssBytes": 67948544, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 76902400, - "startupRssBytes": 65351680, - "loadedRssBytes": 67710976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66949120, - "loadedPeakRssBytes": 68268032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78548992, - "startupRssBytes": 65146880, - "loadedRssBytes": 66981888, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66613248, - "loadedPeakRssBytes": 67817472, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 86102016, - "startupRssBytes": 65794048, - "loadedRssBytes": 68546560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66965504, - "loadedPeakRssBytes": 69246976, - "resourceUsageMaxRssBytes": 85250048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "fire-lizard-cave-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71999488, - "startupRssBytes": 65421312, - "loadedRssBytes": 67125248, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66793472, - "loadedPeakRssBytes": 67944448, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71417856, - "startupRssBytes": 64843776, - "loadedRssBytes": 67334144, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66064384, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79163392, - "startupRssBytes": 65540096, - "loadedRssBytes": 66850816, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66707456, - "loadedPeakRssBytes": 67694592, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 94773248, - "startupRssBytes": 65036288, - "loadedRssBytes": 68706304, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66277376, - "loadedPeakRssBytes": 69341184, - "resourceUsageMaxRssBytes": 94773248, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "silas-tavern-basement-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72122368, - "startupRssBytes": 64565248, - "loadedRssBytes": 66269184, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65400832, - "loadedPeakRssBytes": 66957312, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71405568, - "startupRssBytes": 65630208, - "loadedRssBytes": 67727360, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66318336, - "loadedPeakRssBytes": 68210688, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78974976, - "startupRssBytes": 65765376, - "loadedRssBytes": 67600384, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67031040, - "loadedPeakRssBytes": 68091904, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74752000, - "startupRssBytes": 65855488, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67010560, - "loadedPeakRssBytes": 69316608, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "silas-tavern-storeroom-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71946240, - "startupRssBytes": 66117632, - "loadedRssBytes": 67166208, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67235840, - "loadedPeakRssBytes": 67985408, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71139328, - "startupRssBytes": 65794048, - "loadedRssBytes": 67760128, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67022848, - "loadedPeakRssBytes": 68370432, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79196160, - "startupRssBytes": 65155072, - "loadedRssBytes": 67252224, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66383872, - "loadedPeakRssBytes": 68141056, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78635008, - "startupRssBytes": 64790528, - "loadedRssBytes": 68329472, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66011136, - "loadedPeakRssBytes": 69222400, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "silas-tavern-storeroom-door", - "solvers": { - "candidate": { - "peakRssBytes": 72241152, - "startupRssBytes": 64372736, - "loadedRssBytes": 65290240, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65490944, - "loadedPeakRssBytes": 66146304, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71512064, - "startupRssBytes": 64557056, - "loadedRssBytes": 67440640, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65273856, - "loadedPeakRssBytes": 68132864, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78893056, - "startupRssBytes": 65040384, - "loadedRssBytes": 67399680, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66592768, - "loadedPeakRssBytes": 68124672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75194368, - "startupRssBytes": 65142784, - "loadedRssBytes": 68419584, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66314240, - "loadedPeakRssBytes": 69242880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "jan-pond-house-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71491584, - "startupRssBytes": 65925120, - "loadedRssBytes": 66842624, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66760704, - "loadedPeakRssBytes": 67538944, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70500352, - "startupRssBytes": 65695744, - "loadedRssBytes": 67137536, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66633728, - "loadedPeakRssBytes": 67809280, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78970880, - "startupRssBytes": 65470464, - "loadedRssBytes": 67436544, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66777088, - "loadedPeakRssBytes": 68116480, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 93093888, - "startupRssBytes": 65413120, - "loadedRssBytes": 68034560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66576384, - "loadedPeakRssBytes": 68816896, - "resourceUsageMaxRssBytes": 91906048, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "graham-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72069120, - "startupRssBytes": 64765952, - "loadedRssBytes": 66338816, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65581056, - "loadedPeakRssBytes": 67194880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71299072, - "startupRssBytes": 65204224, - "loadedRssBytes": 67563520, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66428928, - "loadedPeakRssBytes": 68218880, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78872576, - "startupRssBytes": 65208320, - "loadedRssBytes": 67305472, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66457600, - "loadedPeakRssBytes": 68055040, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 79687680, - "startupRssBytes": 65462272, - "loadedRssBytes": 68476928, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66789376, - "loadedPeakRssBytes": 69304320, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "gravo-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72118272, - "startupRssBytes": 64983040, - "loadedRssBytes": 66686976, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66228224, - "loadedPeakRssBytes": 67579904, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70488064, - "startupRssBytes": 65179648, - "loadedRssBytes": 66883584, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66408448, - "loadedPeakRssBytes": 67633152, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79040512, - "startupRssBytes": 65675264, - "loadedRssBytes": 67117056, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66781184, - "loadedPeakRssBytes": 67846144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75771904, - "startupRssBytes": 65347584, - "loadedRssBytes": 68624384, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66322432, - "loadedPeakRssBytes": 69361664, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "diego-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72146944, - "startupRssBytes": 65880064, - "loadedRssBytes": 67190784, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67063808, - "loadedPeakRssBytes": 68005888, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72073216, - "startupRssBytes": 65470464, - "loadedRssBytes": 67567616, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66408448, - "loadedPeakRssBytes": 68186112, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78852096, - "startupRssBytes": 65925120, - "loadedRssBytes": 67366912, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67059712, - "loadedPeakRssBytes": 68120576, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78807040, - "startupRssBytes": 65200128, - "loadedRssBytes": 68345856, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66367488, - "loadedPeakRssBytes": 69296128, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "grim-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72241152, - "startupRssBytes": 65159168, - "loadedRssBytes": 66994176, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66891776, - "loadedPeakRssBytes": 67862528, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71405568, - "startupRssBytes": 65433600, - "loadedRssBytes": 67399680, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66494464, - "loadedPeakRssBytes": 68214784, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78860288, - "startupRssBytes": 65060864, - "loadedRssBytes": 67158016, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66310144, - "loadedPeakRssBytes": 68034560, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75022336, - "startupRssBytes": 65683456, - "loadedRssBytes": 68567040, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67006464, - "loadedPeakRssBytes": 69443584, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "mercenary-near-grim-chest", - "solvers": { - "candidate": { - "peakRssBytes": 71467008, - "startupRssBytes": 65024000, - "loadedRssBytes": 66072576, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66228224, - "loadedPeakRssBytes": 66879488, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71958528, - "startupRssBytes": 65830912, - "loadedRssBytes": 67403776, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66977792, - "loadedPeakRssBytes": 68313088, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78442496, - "startupRssBytes": 65724416, - "loadedRssBytes": 66904064, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66744320, - "loadedPeakRssBytes": 67756032, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 78852096, - "startupRssBytes": 64790528, - "loadedRssBytes": 68591616, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 65409024, - "loadedPeakRssBytes": 69369856, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "popular-lock", - "solvers": { - "candidate": { - "peakRssBytes": 72925184, - "startupRssBytes": 64864256, - "loadedRssBytes": 67223552, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66072576, - "loadedPeakRssBytes": 67854336, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 72818688, - "startupRssBytes": 65572864, - "loadedRssBytes": 67670016, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66842624, - "loadedPeakRssBytes": 68358144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78925824, - "startupRssBytes": 64843776, - "loadedRssBytes": 67465216, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66355200, - "loadedPeakRssBytes": 68124672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75907072, - "startupRssBytes": 65937408, - "loadedRssBytes": 67903488, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66772992, - "loadedPeakRssBytes": 68808704, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "alberto-mine-hut-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72032256, - "startupRssBytes": 65372160, - "loadedRssBytes": 66945024, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66519040, - "loadedPeakRssBytes": 67907584, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71299072, - "startupRssBytes": 66199552, - "loadedRssBytes": 67772416, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67170304, - "loadedPeakRssBytes": 68464640, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78888960, - "startupRssBytes": 65069056, - "loadedRssBytes": 67166208, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66940928, - "loadedPeakRssBytes": 68124672, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 75685888, - "startupRssBytes": 65064960, - "loadedRssBytes": 68472832, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66490368, - "loadedPeakRssBytes": 69316608, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "alberto-mine-upstairs-left-chest", - "solvers": { - "candidate": { - "peakRssBytes": 70963200, - "startupRssBytes": 65175552, - "loadedRssBytes": 67010560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66551808, - "loadedPeakRssBytes": 67665920, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71032832, - "startupRssBytes": 65646592, - "loadedRssBytes": 67481600, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66998272, - "loadedPeakRssBytes": 68358144, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 71507968, - "startupRssBytes": 65769472, - "loadedRssBytes": 67080192, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66801664, - "loadedPeakRssBytes": 67776512, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 72077312, - "startupRssBytes": 65044480, - "loadedRssBytes": 68321280, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66244608, - "loadedPeakRssBytes": 69312512, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "alberto-mine-upstairs-right-chest", - "solvers": { - "candidate": { - "peakRssBytes": 70955008, - "startupRssBytes": 65347584, - "loadedRssBytes": 66658304, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66600960, - "loadedPeakRssBytes": 67461120, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 70983680, - "startupRssBytes": 65761280, - "loadedRssBytes": 67334144, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66994176, - "loadedPeakRssBytes": 68202496, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 78843904, - "startupRssBytes": 65380352, - "loadedRssBytes": 67477504, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66445312, - "loadedPeakRssBytes": 68112384, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 73433088, - "startupRssBytes": 65761280, - "loadedRssBytes": 67727360, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66560000, - "loadedPeakRssBytes": 68489216, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "cavalorn-storeroom-door", - "solvers": { - "candidate": { - "peakRssBytes": 72126464, - "startupRssBytes": 65568768, - "loadedRssBytes": 67010560, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66990080, - "loadedPeakRssBytes": 67907584, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71397376, - "startupRssBytes": 65282048, - "loadedRssBytes": 67379200, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66547712, - "loadedPeakRssBytes": 68235264, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79187968, - "startupRssBytes": 65269760, - "loadedRssBytes": 67366912, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66609152, - "loadedPeakRssBytes": 68096000, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 85778432, - "startupRssBytes": 65150976, - "loadedRssBytes": 68558848, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66166784, - "loadedPeakRssBytes": 69103616, - "resourceUsageMaxRssBytes": 85106688, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - }, - { - "id": "old-to-new-camp-bandits-chest", - "solvers": { - "candidate": { - "peakRssBytes": 72785920, - "startupRssBytes": 65982464, - "loadedRssBytes": 67162112, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 67121152, - "loadedPeakRssBytes": 67997696, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "baseline": { - "peakRssBytes": 71737344, - "startupRssBytes": 65581056, - "loadedRssBytes": 67416064, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66609152, - "loadedPeakRssBytes": 68075520, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "originalBfs": { - "peakRssBytes": 79044608, - "startupRssBytes": 65212416, - "loadedRssBytes": 67309568, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66351104, - "loadedPeakRssBytes": 68112384, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - }, - "upstream": { - "peakRssBytes": 74752000, - "startupRssBytes": 65073152, - "loadedRssBytes": 68218880, - "lockCount": 1, - "peakRssMetric": "linux-proc-VmHWM", - "startupPeakRssBytes": 66187264, - "loadedPeakRssBytes": 69152768, - "resourceUsageMaxRssBytes": 84721664, - "startupResourceUsageMaxRssBytes": 84721664, - "resourceUsageLaunchFloorDetected": true - } - }, - "repetitions": 1, - "verdict": "inconclusive", - "reason": "Single isolated per-lock observation; environmental variation is not estimated per lock." - } - ] -} -``` - -## Environment and source provenance - -```json -{ - "environment": { - "node": "v24.19.0", - "v8": "13.6.233.17-node.51", - "platform": "linux", - "arch": "x64", - "cpu": "AMD EPYC 9V74 80-Core Processor", - "logicalCpus": 9, - "osRelease": "6.18.35", - "totalMemoryBytes": 23109910528, - "execArgv": [] - }, - "source": { - "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", - "dirty": false, - "files": { - "errors.ts": "830e0995bb1d5b98e8ce0495a22e5ae169cab10b2bdff2612e58323c397306f5", - "index.ts": "a1a2eedf5010cd66fc5d451035923e6ab417b36ba84cb2da85789cea17a1569a", - "indexed-heap.ts": "f776755f7405968b567922473c1e6ea5a63dae7643c4bd62b993159e900b7fbc", - "indexed.ts": "279b11d625a033f45723dd94df13a9be9fdfe267cff2535d5470b2011f11e125", - "lock-model.ts": "b4674921412e2acfbf0eaeff2031b3fc3907dffe886c42e588cd5ba6348ec966", - "matrix-analysis.ts": "a105dee650d645a730e3cc7e62a8e9faae31b29ad98c28aafce5ef15df236fc6", - "matrix-search.ts": "380ae745289a7ffd1997a6b9aa7dc3dd06a56e17453732d41042891c42435d3a", - "search-bfs.ts": "011c675973ccc8d753c0b9b3608882b16ca93ef4b460635c3474d8d5fc705feb", - "search-limits.ts": "91afc0940d49af46d9bb57d91af45df992f0684593d2f2d8bc3c13fad3f4c681", - "types.ts": "a2e72cf02e31694c52c1621cf5359863585b8383aa83a7d4e977c8547e8c879b" - }, - "harness": { - "catalog.ts": "2b75c9e9b5fe6997006307bf74cd90d8645d6d61fc527681f209c164680b880a", - "cli.ts": "b717c3d69c13d4a5b9a77589b1465f96338fe9d8be6501249f4470db035efe33", - "comparison.ts": "680ac76d266b51ff1f951fd3e232248bc5df0ab9eb70123285d31d176fb3a709", - "harness.ts": "14d23d697b2c4dd8adc13beac84b1cd3c0236aa34ae42ea96935fb30ac75e9f8", - "memory.ts": "429b45f541a2ac97e46fb6598ac30966cab4e2dca7e355c56f3ad3e037f43d93", - "rss.ts": "acc321bc34deac44819e5ff6d9af89e91c7394d9d897f7d69ce0cfedfc2e992c", - "solver.bench.ts": "985fbba3b387109261abeb1fdcbecd91b0ccfaa4dde5e0f162ad1758d529647f", - "validation.ts": "c44fc2a3de4da26ab207186a97a6b80ab20a910d27ae335da6cec536e402d77d" - }, - "scripts": { - "benchmark-compare.ts": "d4243c84a0a007b611e04e9c07031c3a7e48fb1a610366349084f0a7ab61cd71", - "benchmark-memory-worker.ts": "807707ca177f43c5ef08b88aa1068ad5f08f391b74f7f8d358159a2e5bf8a49b", - "benchmark-memory.ts": "c25a0407122f1c16261d8f8f9fd6f60cba463a7e288579ba67fde398db8d854f", - "benchmark-snapshot.ts": "50b31ffeac808f5b21f0fb864d2efd8a7c758491cf4e2d611bbe900bf3f9ec32", - "benchmark.ts": "fdb3f22dfdd495a6dde6a27fc3b0db68c7ceb75b94acaff131d30a25b6a06f24" - } - }, - "candidate": { - "revision": "1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9", - "moduleSha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b", - "format": "tuple", - "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/dist/gothic-lock-solver.mjs" - }, - "baseline": { - "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", - "moduleSha256": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "format": "legacy", - "module": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/reference-matrix/src/index.mjs", - "moduleTree": { - "cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", - "index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", - "matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", - "matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", - "result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", - "search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", - "solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", - "state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" - }, - "snapshotSha256": "c21b6319523f625439403c0417d267b5a63c35abafebef758bfc6c21217a3b94", - "snapshot": "/workspace/scratch/5d94b8672fa4/gothic-lock-solver-typescript/benchmarks/snapshots/reference-matrix.json", - "kind": "reference-baseline" - }, - "fixtures": { - "count": 45, - "catalogSha256": "0eb5b641eac4bda105bde24b60399220f1767dd13db6a312b78638b2750ba675", - "manifestSha256": "a634d5328fd8d41470397b622ae0bd32b8aac0355dfc31975e023a63a62e65c9" - }, - "originalBfs": { - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "a87e739a22ccc4215d6d9b14fa06b0738171cb60", - "license": "Apache-2.0", - "files": { - "src/index.mjs": "927fc5391c8c5f673f52c35d1fd2bd14afa5d47554a7a410dae0d8c03788c90f", - "src/lock-definition.mjs": "e87e8eae439601cb93d85e8c5d5314976574ad6c6e595fff98f419ef80623d74", - "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "src/solver.mjs": "ab7335383670c40ccbe2a53c6591dc67a04b7abda49d44978c9a790938f92dec", - "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd", - "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" - }, - "notes": "Unmodified source snapshots; imports remain relative within this directory. No upstream UnlockMyLoot solver code is included." - }, - "matrixReference": { - "schemaVersion": 1, - "repository": "https://github.com/Marcus-Rise/gothic-lock-solver", - "revision": "6b1cfbc13bcec68f609d45d5dc97504dd84f1760", - "license": "Apache-2.0", - "files": { - "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", - "src/cli.mjs": "a8ca1332163fa5df64387b1fccc985244f07e99664eba463153700ecd4d672b5", - "src/index.mjs": "5e3d680cbb1ca7bf9437a3800654bc26b1445ec71994bfc1fc70a7d5381aa6ed", - "src/lock-definition.mjs": "cecc13bdb3c760400dda8421e5bdb58e9fdc5bbd46e1782dbd11f94a6de9a8b3", - "src/matrix-analysis.mjs": "7262f85ba882cd8a6ea9cb294be2d2f7a22acd16bc3a963744b9edbdce397986", - "src/matrix-search.mjs": "b018cfdad21aaf52f4350d5d8f0001afed1b41c44643e7fc8c330e6aca40b1d4", - "src/result.mjs": "1a38110c34b696f62b9632cb3de27bb0e07c938cdd11b3ffcb89de8532bf08b0", - "src/search-bfs.mjs": "69f253801d4bb36a9db6887a82a668b0a2765fe55c33cdf7c712fe82df128a75", - "src/search-limits.mjs": "4a302b500fa41019b1ec3d8f3b86ad28de1e2046a8f9ac7a707608170ce7b569", - "src/solver.mjs": "31e3ebfb7035bc57d4c435351642eac0b49820d9ed4682d7eac38b0529dddd29", - "src/state-codec.mjs": "ce37a9ede6b26196fef7b89aa9501fe94c6a18fc4110c1cea394995b053759e4", - "src/transition.mjs": "28cf5007a8fd1db9d179bf233d995a5e88f5a6c486bca22395e2ed144cdbd4fd" - }, - "notes": "Byte-identical Apache matrix reference, copied from source at the pinned revision. Benchmark-only; excluded from runtime and npm package. No external AGPL solver source." - }, - "upstream": { - "revision": "eee0bf50ebcb7fffd2b47954fd76bb015854e365", - "solver": { - "path": "index.html", - "sha256": "691f744099a9169f30e9883fddc5fdbb4949d306f3011e601678419d7b93821b", - "blockSha256": "d5bc85f40fd7c5b61097403df399ecb7dfd2b58924dc7a670aa0d05306017380", - "startMarker": "/* solver-start", - "endMarker": "/* solver-end */" - }, - "measured": true, - "externalCheckout": "/workspace/scratch/5d94b8672fa4/unlockmyloot", - "license": "External AGPL source is hash-verified and executed only from the supplied pinned checkout; no upstream solver code is vendored." - }, - "artifacts": { - "gothic-lock-solver.mjs": { - "bytes": 20143, - "sha256": "247aef3e975df19df3d3372bb43893b112f2cdb17c450e0a95ce05cc7b819b7b" - }, - "gothic-lock-solver.min.mjs": { - "bytes": 9098, - "sha256": "9a7c0d365a0e2630372200f4c8a7914024b04afd048ca77e6a6783c120676d1f" - }, - "gothic-lock-solver.js": { - "bytes": 20916, - "sha256": "14841c4bc1563f4fd8beae3e277798878a2862efa0840e50b1aef9d5e5dff101" - }, - "gothic-lock-solver.min.js": { - "bytes": 9197, - "sha256": "2a936f190ae38f458e2b209681beb099974f32d654eb33be7249be6ede847694" - }, - "gothic-lock-solver.cli.mjs": { - "bytes": 23199, - "sha256": "75129dff219af243b05ffcbdb63ac1a14a32fd0d0b5ae8ee4528bb4d24b75f81" - } - } -} -``` - -## Interpretation - -Every warmup and measured solution is replayed independently one unit division at a time, checked against the stored exact action minimum, input immutability and deterministic paths. Target snapshot changes cannot mask candidate drift: the supplied baseline module must reproduce the supplied baseline snapshot. - -Historical upstream observations have a different objective (unit divisions, then switches); they are never a substitute for live paired timings. U/C/switches are descriptive and are still strict migration regression gates. Timings describe this Node runtime and machine only. A performance inconclusive verdict does not establish absence of regression. Sum of per-lock medians is not a whole-catalog wall-clock median. Raw samples, paths and calibration envelopes are in benchmark.json. diff --git a/docs/development.md b/docs/development.md deleted file mode 100644 index 6112ec2..0000000 --- a/docs/development.md +++ /dev/null @@ -1,135 +0,0 @@ -# Development and architecture - -## Toolchain - -Use pnpm 12.3.4 and Node.js 24 or 26 for development. Exact dependency versions -and integrity hashes are recorded in `package.json` and `pnpm-lock.yaml`. - -| Tool | Pinned version | Responsibility | -| --- | --- | --- | -| TypeScript | 7.0.2 | Strict type checking and declarations | -| Vite | 8.2.2 | All five runtime bundles | -| Vitest | 5.0.0 | Node/browser behavior tests, coverage and developer benchmarks | -| Playwright | 1.63.0 | Real Chromium, Firefox, WebKit consumers | -| Oxlint | 1.81.0 | Static checks; warnings fail | - -Production consumers use emitted JavaScript; they do not install this toolchain. -The supported consumer runtime range is Node.js 22, 24 and 26, declared in -`package.json`. Verification uses their latest maintained patch releases (currently -22.23.2, 24.19.0 and 26.8.1); use the latest patch of your chosen line. Browser testing uses real engines, not jsdom. - -```sh -pnpm install --frozen-lockfile -pnpm exec playwright install --with-deps chromium firefox webkit -pnpm test -pnpm test:coverage -``` - -`pnpm test` runs static checks, Node tests, Vite builds, distribution checks, -browser projects, unchanged-file E2E and clean package verification. Coverage is -also a required release gate. The benchmark is deliberately separate so it can -run without concurrent CPU-heavy test/build tasks: - -```sh -pnpm benchmark --reference ../unlockmyloot --output-dir artifacts/benchmark -``` - -See [the benchmark instructions](../benchmarks/README.md) for pinning upstream, -comparing a PR target, creating snapshots and interpreting noise. - -## Strictness and the dependency patch - -`tsconfig.json` enables the relevant strict flags, checked indexed access, exact -optional properties, explicit type imports, unused checks and library checking. -The build config emits declarations with the same source contract. No `any`, -non-null assertions or suppression comments are permitted in production code. -Negative type tests use `@ts-expect-error` to prove invalid imports/calls fail. -Runtime validation handles JSON, sparse arrays and values that TypeScript cannot -protect at an external boundary. - -Vitest 5.0.0 currently has three declaration incompatibilities under these checks: -its config declaration imports an absent `@vitest/expect` dependency, and the -resolved `provider`/`vmMemoryLimit` option types include explicit `undefined` -while their optional declarations omit it. The narrow checked-in -`patches/vitest@5.0.0.patch` corrects declarations only, using its direct Chai -dependency and matching those option unions. It does not change runtime code or -turn off `skipLibCheck` diagnostics. The official npm tarball was independently -verified against registry integrity before the patch was applied. - -Review this patch on every Vitest update and remove it when upstream declarations -support these checks. Do not silently downgrade the current tools or force an -incompatible peer dependency. The patch is maintained through pnpm's documented -patched-dependency mechanism. - -## Domain boundaries - -`LockModel` owns input invariants and copies. `PreparedSearch` owns encoded effects -and safe state representation. `SearchBudget` and `IndexedHeap` encapsulate state -with operational invariants. Matrix elimination uses pure exact-arithmetic -functions; strategies implement one small internal search contract. The facade -chooses the strategy without exposing the search machinery. - -The core has no filesystem, DOM, UI or network dependency. CLI formatting and IO -live in `cli/`. Benchmarks own their independent replay and reference loading; -production code does not depend on benchmark fixtures. The release scripts -orchestrate already verified files and do not implement a second bundler. - -This applies DDD through explicit domain terms and invariant ownership, BDD -through observable input/outcome scenarios, and TDD through a failing behavioral -check before its implementation. It does not require a DDD framework, Gherkin, -DI container or class for every function. Composition and plain functions keep -the implementation proportionate to a small mathematical library. - -## Tests and evidence - -- Unit and oracle tests protect mechanics, optimal action counts, validation, - resource outcomes and nonmutation. -- The same catalog is replayed on Node and real browser projects. -- Static consumer tests verify named/namespace imports and reject a default export. -- Distribution E2E tests serve unchanged bundle bytes from a static HTTP server, - exercise every format and all 45 inputs, test Workers and classic `file://`, - and detect unexpected runtime requests. -- Clean tarball verification checks package contents, type resolution, imports, - browser use and the installed CLI. A failed required consumer prevents a - package from being marked verified. -- Coverage includes all shipped core and CLI sources, including unimported files; - statements, branches, functions and lines each require at least 80%. - -Each gate has a purpose. Do not add tests that merely mirror private methods, -or change snapshots and thresholds to conceal regressions. Timing, peak RSS, -quality and environment compatibility are separate evidence. A benchmark with -unresolved noise remains inconclusive. - -## Contribution sequence - -1. Read the active specification and tests, then add the failing behavioral case. -2. Implement the smallest change that preserves domain invariants. -3. Run focused tests and static checks; then run the full local production checks. -4. Measure candidate and baseline together without competing benchmark processes. -5. Have an independent reviewer check claims, tests, code and owner principles. -6. Only after local acceptance, put the working commands in official GitHub Actions. - -The migration is one draft PR based on the preserved reference branch. Publishing -and merging are separate future actions. Historical design documents and reports -remain for provenance; current API, algorithm and development docs are linked -from README and AGENTS instead of duplicated into another wiki. - -## Official references - -- [TypeScript handbook](https://www.typescriptlang.org/docs/handbook/intro.html) -- [TypeScript strict configuration](https://www.typescriptlang.org/tsconfig/strict.html) -- [Vite TypeScript behavior](https://vite.dev/guide/features#typescript) -- [Vite build options](https://vite.dev/config/build-options) -- [Vitest Browser Mode](https://vitest.dev/guide/browser/) -- [Vitest coverage](https://vitest.dev/guide/coverage.html) -- [Playwright browsers](https://playwright.dev/docs/browsers) -- [pnpm patched dependencies](https://pnpm.io/settings#patcheddependencies) - -## Independent migration review - -The [2026-09-07 review](reviews/2026-09-07-principles-review.md) records independent -checks of the mathematical contract, owner principles, strict TypeScript, -production consumers and release recovery. It distinguishes executed evidence -from the future first live npm/OIDC/CDN publication. The all-45 timing report -remains inconclusive about per-lock speed equivalence; no acceptance is inferred -from the absence of a confirmed aggregate regression. diff --git a/docs/reviews/2026-09-07-principles-review.md b/docs/reviews/2026-09-07-principles-review.md deleted file mode 100644 index 7af1b2e..0000000 --- a/docs/reviews/2026-09-07-principles-review.md +++ /dev/null @@ -1,240 +0,0 @@ -# Independent engineering review - -Date: 2026-09-07. Scope: the TypeScript library migration, its five distribution -files, tests, benchmark evidence, documentation, release helpers and subsequent -workflow integration. - -**Verdict: the reviewed local implementation passes the required checks.** Three -delivery findings were corrected and verified. Timing equivalence with the -preserved matrix implementation remains **inconclusive**, not a performance pass. -This review does not authorize publication or merging. - -## Reviewed source and evidence - -The measured runtime source is commit -`1651cd478b61f368d2fb7ce4cb0f3f644ade4eb9`. The review also includes the subsequent -release-pagination correction and its regression test. After the independent -full build, all five executable-file hashes still matched the -[clean-source benchmark report](../benchmarks/run-20260907T091221367Z-d505e32e/benchmark.json). -Documentation and delivery-only changes do not change those measured runtime bytes. - -The reviewer independently read the owner's applicable public-safe engineering -principles, including proportional verification, invariant ownership, real -production-artifact checks and separation of deterministic and environmental -verdicts. Private wiki material is not reproduced here. - -## Principles applied to this implementation - -| Principle | Concrete evidence and assessment | -| --- | --- | -| DDD | `LockModel` owns validated state and directed links. `PreparedSearch` owns safe encoding and direct effects. Pin positions, numeric displacements, actions and search limits have explicit meanings. CLI/UI coordinates stay outside the mathematical model. | -| SOLID | Model validation, exact arithmetic, search budgets, priority queues, search strategies and file IO have distinct responsibilities. The public facade has two inputs. Internal strategies share a small contract; no public strategy registry is needed. | -| KISS / YAGNI | No DI container, repository layer, event bus, plugin mechanism or solver-instance wrapper was added. Stateful classes encapsulate actual state; rational operations remain functions. Vite builds the files; release helpers coordinate official CLIs and verified artifacts. | -| DRY | Production mechanics have one owner. CLI delegates to the core. The separate test simulator deliberately does not reuse production transitions: that independence can expose a shared implementation mistake. Preserved reference code is benchmark-only and excluded from the package. | -| BDD | Tests express observable results: signed asymmetric links, no cascade, blocked motion, nonmutation, already-open versus unreachable, distinct resource errors, installed imports and CLI exit codes. A Gherkin dependency would not strengthen these scenarios. | -| TDD | New contract and distribution tests were written before the corresponding entries/artifacts existed. The available historical red evidence has the limits stated below. Review findings received failing regression tests before fixes. | -| Evidence-driven verification | The reviewer executed the complete local production check, inspected the package-consumer matrix log, recomputed benchmark totals and verified report hashes. Author summaries alone were not treated as a passing gate. | - -The complexity and patterns are proportionate to an exact mathematical library -with multiple distribution environments. The review found no remaining concrete -mathematical correctness defect in the inspected implementation. - -## Mathematics and contract - -The implementation and [algorithm document](../algorithm.md) agree on -`M = I + linksᵀ`, direct noncascading effects and numeric pin displacement. -Endpoint bounds suffice because each affected position moves monotonically -within a command; independent replay nevertheless checks every unit shift. - -Exact rational elimination establishes necessary algebraic conditions. A -successful greedy certificate attains a lower bound; failed scheduling falls -through to search. For invertible M, an action changes one residual coordinate -by at most six, making `sum(ceil(abs(r[i])/6))` consistent. A* and the singular -case's unit-cost BFS therefore minimize grouped actions when they return a path. - -The documentation correctly avoids promising minimum unit shifts or distinct -selected plates. It separates the N≤18 safe-number representation limit from -the game's model, and distinguishes resource failure from proved impossibility. -Its complexity table includes exponential state space, dense initialization, -frontier storage and the distinction between rational-operation and bit costs. - -The 45 catalog cases are supported by independent unit-step replay and preserved -minimum-action results. All 441 two-plate configurations and 192 seeded -three-plate inputs are additionally checked against an independent exhaustive -oracle. These tests complement the optimality argument; their count is not a -proof over every larger input. - -## TypeScript and JavaScript review - -The pinned compiler is TypeScript 7.0.2. Its use was checked against the -[official TypeScript 7 announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/) -and current handbook guidance. The code uses ordinary JavaScript control flow -with explicit narrowing of unknown external data, as described in -[Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html). -There are no production `any` types, non-null assertions or unchecked type casts. - -Readonly tuples and mapped types express the known dimensions; runtime checks -handle dynamic arrays and JSON. Type-only imports, explicit module exports and -installed declaration-consumer tests agree with the -[module reference](https://www.typescriptlang.org/docs/handbook/modules/reference.html). -Strictness includes checked indexed access, exact optional properties and library -checking; see the [compiler option reference](https://www.typescriptlang.org/tsconfig/). - -Class fields with explicit constructor assignments remain current TypeScript. -Using every new syntax feature would not improve this contract; the -[classes handbook](https://www.typescriptlang.org/docs/handbook/2/classes.html) -supports the chosen composition of classes and functions. - -One compatibility exception is explicit: Vitest 5.0.0 needs a narrow declaration -patch under the selected strict checks. It corrects an absent type dependency -and optional-property unions, without changing runtime behavior or disabling -library checking. The rationale and removal condition are documented in -[development guidance](../development.md#strictness-and-the-dependency-patch). -This is not presented as an untouched upstream declaration package. - -## Independent local execution - -The reviewer ran the repository's actual `pnpm test` command, covering the entire -local pipeline, before GitHub workflow integration. A PTY capture preserved the -continuous output and final process status. The run exited **0**. - -| Gate | Observed result | -| --- | --- | -| Oxlint and strict TypeScript | Passed | -| Node behavior/coverage tests | 170 tests passed | -| Vite build | All five executable files built | -| Distribution tests | 13 tests passed | -| Vitest browser projects | 324 tests passed across Chromium, Firefox and WebKit | -| Static-file E2E | 24 scenarios passed, including all 45 locks for each of four core formats | -| Installed tarball | Type resolution, named/namespace imports and CLI exit codes 0/1/2 passed; all 24 browser scenarios passed again against installed package bytes | - -Browser versions were Chromium 153.0.8010.12, Firefox 155.0 and WebKit 26.6. -The reviewer directly ran Node 24.19.0. The separately captured package matrix -was inspected and showed the identical tarball passing Node 22.23.2 and 26.8.1 -consumer and CLI checks as well. - -Coverage includes all production `src/` and `cli/` sources, including unimported -files. The aggregate gate is 80% for each metric, not 80% per individual file. - -| Metric | Measured | Required | -| --- | ---: | ---: | -| Statements | 95.44% | 80% | -| Branches | 91.59% | 80% | -| Functions | 100% | 80% | -| Lines | 98.38% | 80% | - -The CLI entry's single top-level statement is exercised through subprocesses and -is still included as uncovered in the in-process coverage report. It was not -excluded to increase the percentage. The author also demonstrated a failing -100% threshold probe while its tests passed. Threshold semantics follow -[Vitest's coverage documentation](https://vitest.dev/config/coverage.html). - -Execution capture: `artifacts/verification/reviewer-full-test.log`, SHA-256 -`f588a5eee4a8c9aab920ed3560871f020b475b157b4336c32808aabacdc9d068`. -The verified npm tarball SHA-256 was -`cc9471e3958394d7dec04c17b1089b9849f675cf915601a88d5bb6a5ff883e19`. -These are evidence for this run, not permanent hashes for future releases. - -## TDD evidence and its limits - -The core author's recorded initial failure was an import of the missing -`src/index.ts`; a second probe called the new two-input contract against the -preserved old API and failed in its option validator. The second probe did not -reach its result assertion. Vitest was unavailable at that point, so a separate -Vitest red assertion was not captured for every new core test. - -Distribution testing recorded missing-file failures before the four bundles -existed, and a further missing-CLI failure before adding the fifth file. -Delivery review corrections recorded failures for semantic evidence tampering, -recovery and pagination before the corresponding fixes. This supports test-first -work, while falling short of a complete independently recorded red/green history -for every line of the migration. A passing final suite cannot reconstruct that -missing history, and this review does not claim otherwise. - -## Corrected review findings - -1. **Publishing-side evidence gate.** Prepared-artifact verification originally - checked hashes without rechecking benchmark semantics. It now validates - quality, performance acceptance, candidate source/module identity, linked - report hashes and agreement with the detailed report. Tests recompute the - manifest digest after changing evidence and still require rejection. Any - performance acceptance must come from the trusted caller for the exact - evidence hash; an artifact cannot approve itself. -2. **Older stable-release recovery.** Finalizing an older draft could promote it - above a newer stable release. Promotion now considers npm's latest version - and published stable versions; recovery tests retain the newer latest. -3. **Release-list pagination.** An existing release beyond the first 100 entries - could be mistaken for a missing release. The official `gh api --paginate - --slurp` result is validated and flattened before existence and promotion - decisions. A test places the release after 100 canaries and verifies recovery. - -Transport tests exercise absent versus unavailable registry responses, exact -existing tarball resumption, conflicting bytes, CDN MIME/CORS/hash rejection, -tag conflicts and draft versus published-asset behavior. They mock only external -transport/CLI boundaries and do not claim that npm publication happened locally. - -## Benchmark assessment and remaining limits - -The reviewer independently verified the JSON/Markdown report hashes, matching -evidence envelopes, 45 unique locks and all aggregate action metrics in the -[full report](../benchmarks/run-20260907T091221367Z-d505e32e/benchmark.md). - -| Implementation | Actions A | Distinct plates U | Unit shifts C | -| --- | ---: | ---: | ---: | -| Typed candidate | 483 | 245 | 1723 | -| Preserved matrix implementation | 483 | 245 | 1723 | -| Original BFS | 483 | 245 | 1727 | -| Pinned Unlock My Loot | 491 | 245 | 1695 | - -Against the upstream objective, the candidate uses fewer actions on five locks -and ties on 40, with no action-count loss. It uses more unit shifts in aggregate; -the documentation must retain that tradeoff. - -Against the matrix reference, timing produced six passed and 39 inconclusive -per-lock verdicts, with no confirmed regression. This does **not** prove timing -equivalence. Both attempts of bounded repeats remain recorded. Memory passed its -calibrated comparison, but the measured Linux RSS high-water includes runtime -and harness allocation; it is not exact solver heap usage or browser memory. - -Live GitHub Actions execution, npm trusted-publisher/OIDC configuration and -post-publication CDN delivery require the future authorized release environment. -The local tests establish the executable preparation and verification paths; -they do not establish that an unpublished package already resolves through a CDN. -Workflow configuration was added and reviewed after this passing local gate, as -described below. No main merge, npm publication or upstream-project modification -was performed as part of this review. - -## Workflow integration review - -The independent full local run finished successfully before `.github/workflows/` -was introduced. The reviewer then inspected both workflows and Dependabot -configuration, parsed the YAML, checked every shell block with `bash -n` and -verified action pins and permission scopes, and independently ran standalone -actionlint 1.7.12. These checks passed; no third-party workflow action was added. - -- CI uses the proven local commands, preserves coverage/package/browser reports, - and compares the built candidate with a separate checkout of the actual PR - target SHA. A candidate snapshot cannot replace that target implementation. -- Each `uses:` entry belongs to GitHub's `actions` organization or pnpm's own - action, pinned to the reviewed complete commit SHA. Dependabot tracks Actions - and npm dependency updates. -- Release preparation produces one verified archive, measures it, saves immutable - raw inputs, then validates the saved evidence without rebuilding or remeasuring. - Job outputs identify the particular artifact needed for a failed-job retry. -- Candidate code runs in a job with read-only repository permissions. The npm - and GitHub publication jobs check out the trusted main workflow source and do - not install or execute candidate package dependencies. Only the npm job receives - OIDC token permission; only GitHub finalization receives repository write - permission. Checkout does not persist credentials. -- The publication order is npm, exact registry/CDN verification with real browser - consumers, then GitHub Release finalization. Partial failure keeps the existing - immutable version available for verified resumption. Canaries do not promote - stable tags, and older stable recovery does not replace a newer latest release. -- Inconclusive or regressed timing is not silently converted to a pass. Preparation - requires a passed comparison or an explicit trusted acceptance tied to the exact - saved evidence hash; quality regressions remain blocking. Initial publication - readiness is disabled until the owner configures npm trusted publishing. - -No new blocking workflow finding remained in this static review. Hosted execution -and live OIDC/CDN publication are separate evidence surfaces; YAML validation and -local mocked transport tests do not substitute for their eventual execution. diff --git a/docs/superpowers/plans/2026-08-31-gothic-lock-solver.md b/docs/superpowers/plans/2026-08-31-gothic-lock-solver.md deleted file mode 100644 index a477f22..0000000 --- a/docs/superpowers/plans/2026-08-31-gothic-lock-solver.md +++ /dev/null @@ -1,1430 +0,0 @@ -# План реализации решателя замков Gothic 1 Remake - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Создать проверяемую Node.js CLI-утилиту, которая по состоянию замка и направленной матрице связей находит минимальную последовательность сгруппированных команд `пластина + направление + xN`. - -**Status:** Выполнен 2026-08-31. Все задачи реализованы и проверены. - -**Architecture:** Вычислительное ядро состоит из чистых ESM-модулей: валидация модели, кодирование состояния, переходы и поиск в ширину. CLI отвечает только за файлы, аргументы, форматирование и коды выхода; благодаря этому ядро позднее можно напрямую использовать на сайте. - -**Tech Stack:** Node.js 20+, pnpm, JavaScript ESM (`.mjs`), встроенные `node:test`, `node:assert`, `node:fs`, `node:child_process`; сторонних runtime- и test-зависимостей нет. - -**Spec:** `../specs/2026-08-31-gothic-lock-solver.md` - -## Global Constraints - -- Число пластин: от 2 до 7; у каждой ровно 7 позиций. -- Цель: все пластины в позиции `4`. -- Влево означает `+1`, вправо означает `-1`. -- Матрица направленная, коэффициенты только `-1`, `0`, `1`, диагональ только `0`. -- Связи применяются один раз от выбранной строки, без обратного вывода связей и без каскада. -- Если хотя бы одна затронутая пластина не может пройти все `steps`, команда полностью заблокирована. -- Стоимость каждого допустимого макрохода `x1..x6` равна одной команде. -- Среди кратчайших решений порядок действий: пластина по возрастанию, `left` перед `right`, большее `steps` перед меньшим. -- Состояния кодируются числами в системе счисления по основанию 7; BFS использует типизированные массивы. -- Ядро не читает и не пишет файлы и не зависит от CLI. -- Пользовательские сообщения CLI выводятся по-русски; значения `direction` в JSON: только `left` и `right`. -- Грамматика CLI строгая: `` либо ` --output `; неизвестные флаги и лишние аргументы дают код `1`. -- Дополнительные свойства входного JSON игнорируются; нормализованная модель содержит только `state` и `links`. -- Обычный результат идет в stdout, ошибки - в stderr; `unsolvable` с `--output` сначала записывает JSON, затем завершает процесс кодом `2`. -- PDF не используется. Сайт, распознавание изображений, база известных замков и преждевременная оптимизация не входят в план. -- Разработка идет по TDD: сначала наблюдаемый правильный провал теста, затем минимальная реализация, затем полный зеленый прогон. - ---- - -## Карта файлов - -| Файл | Ответственность | -|---|---| -| `package.json` | Метаданные Node.js, команды `test` и `solve`, ограничение Node.js 20+. | -| `.gitignore` | Исключение временных данных, зависимостей и приложенных исходных материалов. | -| `solve-lock.mjs` | Минимальная исполняемая точка входа. | -| `src/lock-definition.mjs` | Разбор JSON и полная валидация определения замка. | -| `src/state-codec.mjs` | Кодирование и декодирование состояний в base-7. | -| `src/transition.mjs` | Атомарное применение команды и генерация переходов в стабильном порядке. | -| `src/solver.mjs` | BFS, массивы родителей и восстановление кратчайшего пути. | -| `src/result.mjs` | Формирование JSON-результата, метрик и русского консольного текста. | -| `src/index.mjs` | Стабильный website-safe facade `solveLock` вычислительного ядра. | -| `src/cli.mjs` | Разбор аргументов, чтение/запись файлов и коды завершения. | -| `test/*.test.mjs` | Модульные, дифференциальные и интеграционные тесты. | -| `examples/lock.example.json` | Проверяемый пример входного замка. | -| `scripts/benchmark.mjs` | Ручное измерение предельного семипластинчатого случая. | -| `.codex/skills/gothic-lock-solver/` | Проектный skill интерактивного сбора модели и запуска решателя. | - ---- - -### Task 1: Каркас проекта и валидация определения замка - -**Files:** -- Create: `package.json` -- Create: `.gitignore` -- Create: `src/lock-definition.mjs` -- Create: `test/lock-definition.test.mjs` -- Include in first commit: `docs/superpowers/specs/2026-08-31-gothic-lock-solver.md` -- Include in first commit: `docs/superpowers/plans/2026-08-31-gothic-lock-solver.md` - -**Interfaces:** -- Consumes: строку JSON или неизвестное JavaScript-значение. -- Produces: `LockInputError`, `parseLockDefinition(text)`, `validateLockDefinition(value)`. -- Return shape: `{ state: number[], links: number[][] }`; функции возвращают защитные копии массивов. - -- [x] **Step 1: Подготовить Git без затрагивания приложенного PDF** - -Run: - -```bash -git rev-parse --is-inside-work-tree -``` - -Expected: если команда завершается ошибкой, выполнить `git init`. Не добавлять `project_sources/` в индекс. - -Run: - -```bash -mkdir -p src test -``` - -Expected: каталоги для первых production- и test-файлов существуют. - -- [x] **Step 2: Создать падающие тесты валидации** - -```js -// test/lock-definition.test.mjs -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { - LockInputError, - parseLockDefinition, - validateLockDefinition, -} from '../src/lock-definition.mjs'; - -const validLock = { - state: [7, 4, 1], - links: [ - [0, 1, -1], - [0, 0, 0], - [1, 0, 0], - ], -}; - -test('validateLockDefinition accepts and copies a valid directed lock', () => { - const result = validateLockDefinition(validLock); - assert.deepEqual(result, validLock); - assert.notEqual(result.state, validLock.state); - assert.notEqual(result.links[0], validLock.links[0]); -}); - -for (const [name, value, message] of [ - ['requires 2 to 7 plates', { state: [4], links: [[0]] }, /от 2 до 7/], - ['rejects a position outside 1..7', { ...validLock, state: [7, 8, 1] }, /state\[1\]/], - ['requires a square matrix', { ...validLock, links: [[0], [0], [0]] }, /links\[0\]/], - ['rejects an unknown coefficient', { ...validLock, links: [[0, 2, 0], [0, 0, 0], [0, 0, 0]] }, /-1, 0 или 1/], - ['requires a zero diagonal', { ...validLock, links: [[1, 0, 0], [0, 0, 0], [0, 0, 0]] }, /диагонали/], -]) { - test(name, () => { - assert.throws(() => validateLockDefinition(value), message); - }); -} - -test('parseLockDefinition reports malformed JSON as LockInputError', () => { - assert.throws( - () => parseLockDefinition('{'), - (error) => error instanceof LockInputError && /Некорректный JSON/.test(error.message), - ); -}); -``` - -- [x] **Step 3: Запустить тест и увидеть правильный RED** - -Run: - -```bash -node --test test/lock-definition.test.mjs -``` - -Expected: FAIL с `ERR_MODULE_NOT_FOUND` для `src/lock-definition.mjs`. - -- [x] **Step 4: Создать минимальный проект и реализацию валидации** - -```json -{ - "name": "gothic-lock-solver", - "version": "0.1.0", - "private": true, - "type": "module", - "engines": { - "node": ">=20" - }, - "scripts": { - "test": "node --test" - } -} -``` - -```gitignore -# .gitignore -node_modules/ -coverage/ -*.tmp -project_sources/ -``` - -```js -// src/lock-definition.mjs -export class LockInputError extends Error { - constructor(message, options) { - super(message, options); - this.name = 'LockInputError'; - } -} - -export function validateLockDefinition(value) { - if (value === null || typeof value !== 'object' || Array.isArray(value)) { - throw new LockInputError('Определение замка должно быть JSON-объектом.'); - } - - const { state, links } = value; - if (!Array.isArray(state) || state.length < 2 || state.length > 7) { - throw new LockInputError('state должен содержать от 2 до 7 пластин.'); - } - - state.forEach((position, index) => { - if (!Number.isInteger(position) || position < 1 || position > 7) { - throw new LockInputError(`state[${index}] должен быть целым числом от 1 до 7.`); - } - }); - - if (!Array.isArray(links) || links.length !== state.length) { - throw new LockInputError(`links должен быть матрицей ${state.length}x${state.length}.`); - } - - links.forEach((row, rowIndex) => { - if (!Array.isArray(row) || row.length !== state.length) { - throw new LockInputError(`links[${rowIndex}] должен содержать ${state.length} элементов.`); - } - row.forEach((coefficient, columnIndex) => { - if (![ -1, 0, 1 ].includes(coefficient)) { - throw new LockInputError(`links[${rowIndex}][${columnIndex}] должен быть -1, 0 или 1.`); - } - if (rowIndex === columnIndex && coefficient !== 0) { - throw new LockInputError(`links[${rowIndex}][${columnIndex}] на диагонали должен быть 0.`); - } - }); - }); - - return { - state: [...state], - links: links.map((row) => [...row]), - }; -} - -export function parseLockDefinition(text) { - try { - return validateLockDefinition(JSON.parse(text)); - } catch (error) { - if (error instanceof LockInputError) { - throw error; - } - throw new LockInputError(`Некорректный JSON: ${error.message}`, { cause: error }); - } -} -``` - -- [x] **Step 5: Запустить тесты и увидеть GREEN** - -Run: - -```bash -pnpm test -``` - -Expected: все тесты `lock-definition` PASS. - -- [x] **Step 6: Зафиксировать первый самостоятельно проверяемый результат** - -```bash -git add .gitignore package.json docs/superpowers/specs/2026-08-31-gothic-lock-solver.md docs/superpowers/plans/2026-08-31-gothic-lock-solver.md src/lock-definition.mjs test/lock-definition.test.mjs -git commit -m "feat: validate Gothic lock definitions" -``` - ---- - -### Task 2: Числовой кодек состояний - -**Files:** -- Create: `src/state-codec.mjs` -- Create: `test/state-codec.test.mjs` - -**Interfaces:** -- Consumes: `state: number[]`, `code: number`, `plateCount: number`. -- Produces: `stateSpaceSize(plateCount)`, `encodeState(state)`, `decodeState(code, plateCount)`, `createTargetState(plateCount)`. -- Encoding invariant: `[1, 1] -> 0`, `[7, 7] -> 48`, любой валидный вектор проходит round-trip без изменений. -- Порядок разрядов: `state[0]` - старший base-7 разряд; обязательный `plateCount` при декодировании сохраняет ведущие позиции `1`. - -- [x] **Step 1: Написать падающий тест кодека** - -```js -// test/state-codec.test.mjs -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { - createTargetState, - decodeState, - encodeState, - stateSpaceSize, -} from '../src/state-codec.mjs'; - -test('base-7 encoding has stable boundary values', () => { - assert.equal(encodeState([1, 1]), 0); - assert.equal(encodeState([7, 7]), 48); - assert.equal(stateSpaceSize(7), 823_543); -}); - -test('encodeState and decodeState round-trip a seven-plate state', () => { - const state = [7, 4, 1, 3, 6, 2, 5]; - assert.deepEqual(decodeState(encodeState(state), state.length), state); -}); - -test('createTargetState creates only center positions', () => { - assert.deepEqual(createTargetState(5), [4, 4, 4, 4, 4]); -}); -``` - -- [x] **Step 2: Подтвердить RED** - -Run: `node --test test/state-codec.test.mjs` -Expected: FAIL с `ERR_MODULE_NOT_FOUND`. - -- [x] **Step 3: Реализовать кодек без файловых и CLI-зависимостей** - -```js -// src/state-codec.mjs -export function stateSpaceSize(plateCount) { - return 7 ** plateCount; -} - -export function encodeState(state) { - let code = 0; - for (const position of state) { - code = code * 7 + (position - 1); - } - return code; -} - -export function decodeState(code, plateCount) { - const state = new Array(plateCount); - let remainder = code; - for (let index = plateCount - 1; index >= 0; index -= 1) { - state[index] = (remainder % 7) + 1; - remainder = Math.floor(remainder / 7); - } - return state; -} - -export function createTargetState(plateCount) { - return Array.from({ length: plateCount }, () => 4); -} -``` - -- [x] **Step 4: Подтвердить GREEN и отсутствие регрессии** - -Run: `pnpm test` -Expected: тесты `lock-definition` и `state-codec` PASS. - -- [x] **Step 5: Commit** - -```bash -git add src/state-codec.mjs test/state-codec.test.mjs -git commit -m "feat: encode lock states as base-7 integers" -``` - ---- - -### Task 3: Атомарные макрокоманды и стабильный генератор переходов - -**Files:** -- Create: `src/transition.mjs` -- Create: `test/transition.test.mjs` - -**Interfaces:** -- Consumes: `state`, `links`, `command`. -- Produces: `applyCommand(state, links, command): number[] | null`. -- Produces: `generateTransitions(state, links): Array<{ command, state }>`. -- Public command shape: `{ plate: number, direction: 'left' | 'right', steps: number }`; `plate` is one-based, `steps` is 1..6. -- `null` means the whole command is blocked; input arrays remain unchanged. - -- [x] **Step 1: Написать падающие тесты направления, инверсии, отсутствия каскада и блокировки** - -```js -// test/transition.test.mjs -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { applyCommand, generateTransitions } from '../src/transition.mjs'; - -test('applyCommand moves direct and inverse links by the full step count', () => { - const state = [3, 5, 4]; - const links = [ - [0, 1, -1], - [0, 0, 0], - [0, 0, 0], - ]; - const result = applyCommand(state, links, { plate: 1, direction: 'left', steps: 2 }); - assert.deepEqual(result, [5, 7, 2]); - assert.deepEqual(state, [3, 5, 4]); -}); - -test('applyCommand does not cascade through a moved target row', () => { - const links = [ - [0, 1, 0], - [0, 0, 1], - [0, 0, 0], - ]; - assert.deepEqual( - applyCommand([3, 5, 4], links, { plate: 1, direction: 'left', steps: 2 }), - [5, 7, 4], - ); -}); - -test('applyCommand atomically blocks when one linked plate cannot finish', () => { - const state = [3, 6]; - const links = [[0, 1], [0, 0]]; - assert.deepEqual( - applyCommand(state, links, { plate: 1, direction: 'left', steps: 1 }), - [4, 7], - ); - assert.equal( - applyCommand(state, links, { plate: 1, direction: 'left', steps: 2 }), - null, - ); - assert.deepEqual(state, [3, 6]); -}); - -test('a directed source row does not imply a reverse link', () => { - const links = [[0, 1], [0, 0]]; - assert.deepEqual( - applyCommand([3, 5], links, { plate: 2, direction: 'right', steps: 1 }), - [3, 4], - ); -}); - -test('generateTransitions uses plate, direction, descending steps order', () => { - const transitions = generateTransitions([4, 4], [[0, 0], [0, 0]]); - assert.deepEqual( - transitions.slice(0, 6).map(({ command }) => command), - [ - { plate: 1, direction: 'left', steps: 3 }, - { plate: 1, direction: 'left', steps: 2 }, - { plate: 1, direction: 'left', steps: 1 }, - { plate: 1, direction: 'right', steps: 3 }, - { plate: 1, direction: 'right', steps: 2 }, - { plate: 1, direction: 'right', steps: 1 }, - ], - ); -}); -``` - -- [x] **Step 2: Подтвердить RED** - -Run: `node --test test/transition.test.mjs` -Expected: FAIL с `ERR_MODULE_NOT_FOUND`. - -- [x] **Step 3: Реализовать один атомарный переход и генератор** - -```js -// src/transition.mjs -const DIRECTIONS = ['left', 'right']; - -export function applyCommand(state, links, command) { - const sourceIndex = command.plate - 1; - const directionDelta = command.direction === 'left' ? 1 : -1; - const nextState = [...state]; - - for (let targetIndex = 0; targetIndex < state.length; targetIndex += 1) { - const coefficient = targetIndex === sourceIndex - ? 1 - : links[sourceIndex][targetIndex]; - - if (coefficient === 0) { - continue; - } - - const nextPosition = state[targetIndex] - + directionDelta * coefficient * command.steps; - if (nextPosition < 1 || nextPosition > 7) { - return null; - } - nextState[targetIndex] = nextPosition; - } - - return nextState; -} - -export function generateTransitions(state, links) { - const transitions = []; - for (let plate = 1; plate <= state.length; plate += 1) { - const sourcePosition = state[plate - 1]; - for (const direction of DIRECTIONS) { - const sourceLimit = direction === 'left' - ? 7 - sourcePosition - : sourcePosition - 1; - for (let steps = sourceLimit; steps >= 1; steps -= 1) { - const command = { plate, direction, steps }; - const nextState = applyCommand(state, links, command); - if (nextState !== null) { - transitions.push({ command, state: nextState }); - } - } - } - } - return transitions; -} -``` - -- [x] **Step 4: Подтвердить GREEN полным прогоном** - -Run: `pnpm test` -Expected: все тесты PASS. - -- [x] **Step 5: Commit** - -```bash -git add src/transition.mjs test/transition.test.mjs -git commit -m "feat: model atomic linked plate commands" -``` - ---- - -### Task 4: BFS и восстановление минимальной комбинации - -**Files:** -- Create: `src/solver.mjs` -- Create: `test/solver.test.mjs` - -**Interfaces:** -- Consumes: проверенный объект `{ state, links }`. -- Produces: `findShortestCommands(definition): Command[] | null`. -- `[]` означает уже открытый замок; `null` означает недостижимую цель. -- Внутренние `packCommand`/`unpackCommand` кодируют одну команду в `Uint8Array`. - -- [x] **Step 1: Написать падающие тесты готовой цели, макрооптимальности, tie-break и недостижимости** - -```js -// test/solver.test.mjs -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { - findShortestCommands, - packCommand, - unpackCommand, -} from '../src/solver.mjs'; - -const noLinks = [[0, 0], [0, 0]]; - -test('packs every possible command into one byte and restores it', () => { - for (let plate = 1; plate <= 7; plate += 1) { - for (const direction of ['left', 'right']) { - for (let steps = 1; steps <= 6; steps += 1) { - const command = { plate, direction, steps }; - const packed = packCommand(command); - assert.ok(packed >= 0 && packed <= 83); - assert.deepEqual(unpackCommand(packed), command); - } - } - } -}); - -test('returns no commands for an already open lock', () => { - assert.deepEqual( - findShortestCommands({ state: [4, 4], links: noLinks }), - [], - ); -}); - -test('uses one macro command per independent plate', () => { - assert.deepEqual( - findShortestCommands({ state: [1, 7], links: noLinks }), - [ - { plate: 1, direction: 'left', steps: 3 }, - { plate: 2, direction: 'right', steps: 3 }, - ], - ); -}); - -test('prefers the lower plate when two one-command solutions exist', () => { - const links = [[0, 1], [1, 0]]; - assert.deepEqual( - findShortestCommands({ state: [1, 1], links }), - [{ plate: 1, direction: 'left', steps: 3 }], - ); -}); - -test('returns null when every possible move is blocked', () => { - const links = [[0, 1], [1, 0]]; - assert.equal(findShortestCommands({ state: [1, 7], links }), null); -}); - -test('prefers left over right when both start equally short solutions', () => { - const definition = { - state: [2, 7, 1], - links: [ - [0, 0, 0], - [-1, 0, -1], - [-1, -1, 0], - ], - }; - assert.deepEqual(findShortestCommands(definition), [ - { plate: 1, direction: 'left', steps: 5 }, - { plate: 3, direction: 'left', steps: 3 }, - ]); -}); - -test('prefers larger steps when earlier action fields are equal', () => { - const definition = { - state: [3, 2, 1], - links: [ - [0, 0, -1], - [-1, 0, -1], - [-1, -1, 0], - ], - }; - assert.deepEqual(findShortestCommands(definition), [ - { plate: 1, direction: 'right', steps: 2 }, - { plate: 3, direction: 'right', steps: 2 }, - { plate: 2, direction: 'right', steps: 2 }, - { plate: 1, direction: 'right', steps: 3 }, - { plate: 3, direction: 'right', steps: 2 }, - ]); -}); -``` - -- [x] **Step 2: Подтвердить RED** - -Run: `node --test test/solver.test.mjs` -Expected: FAIL с `ERR_MODULE_NOT_FOUND`. - -- [x] **Step 3: Реализовать упаковку команды и восстановление пути** - -```js -// начало src/solver.mjs -import { - createTargetState, - decodeState, - encodeState, - stateSpaceSize, -} from './state-codec.mjs'; -import { generateTransitions } from './transition.mjs'; - -const UNSEEN = -2; -const ROOT = -1; - -export function packCommand(command) { - const directionBit = command.direction === 'left' ? 0 : 1; - return (((command.plate - 1) * 2 + directionBit) * 6) + command.steps - 1; -} - -export function unpackCommand(packed) { - const steps = (packed % 6) + 1; - const plateDirection = Math.floor(packed / 6); - return { - plate: Math.floor(plateDirection / 2) + 1, - direction: plateDirection % 2 === 0 ? 'left' : 'right', - steps, - }; -} - -function reconstructCommands(startCode, targetCode, parents, actions) { - const commands = []; - let code = targetCode; - while (code !== startCode) { - commands.push(unpackCommand(actions[code])); - code = parents[code]; - } - commands.reverse(); - return commands; -} -``` - -- [x] **Step 4: Реализовать минимальный BFS на типизированных массивах** - -```js -// продолжение src/solver.mjs -export function findShortestCommands({ state, links }) { - const plateCount = state.length; - const startCode = encodeState(state); - const targetCode = encodeState(createTargetState(plateCount)); - if (startCode === targetCode) { - return []; - } - - const size = stateSpaceSize(plateCount); - const parents = new Int32Array(size); - parents.fill(UNSEEN); - const actions = new Uint8Array(size); - const queue = new Int32Array(size); - - parents[startCode] = ROOT; - let head = 0; - let tail = 0; - queue[tail] = startCode; - tail += 1; - - while (head < tail) { - const currentCode = queue[head]; - head += 1; - const currentState = decodeState(currentCode, plateCount); - - for (const transition of generateTransitions(currentState, links)) { - const nextCode = encodeState(transition.state); - if (parents[nextCode] !== UNSEEN) { - continue; - } - - parents[nextCode] = currentCode; - actions[nextCode] = packCommand(transition.command); - if (nextCode === targetCode) { - return reconstructCommands(startCode, targetCode, parents, actions); - } - queue[tail] = nextCode; - tail += 1; - } - } - - return null; -} -``` - -- [x] **Step 5: Подтвердить кратчайший и стабильный GREEN** - -Run: `pnpm test` -Expected: все тесты PASS; независимый замок решается двумя командами, а равный однокомандный путь выбирает пластину 1. - -- [x] **Step 6: Commit** - -```bash -git add src/solver.mjs test/solver.test.mjs -git commit -m "feat: find shortest lock command sequence" -``` - ---- - -### Task 5: Результат, метрики и консольное представление - -**Files:** -- Create: `src/result.mjs` -- Create: `src/index.mjs` -- Create: `test/result.test.mjs` - -**Interfaces:** -- Consumes: проверенное определение замка и `Command[] | null`. -- Produces: `createSolveResult(definition, commands)` строго по JSON-структуре спеки. -- Produces: `formatConsoleResult(result): string` без завершающего перевода строки. -- Produces: `solveLock(value)` как единственную стабильную высокоуровневую функцию для будущего сайта. - -- [x] **Step 1: Написать падающие тесты solved, already-open и unsolvable** - -```js -// test/result.test.mjs -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { solveLock } from '../src/index.mjs'; -import { createSolveResult, formatConsoleResult } from '../src/result.mjs'; - -const definition = { - state: [1, 7], - links: [[0, 0], [0, 0]], -}; -const commands = [ - { plate: 1, direction: 'left', steps: 3 }, - { plate: 2, direction: 'right', steps: 3 }, -]; - -test('createSolveResult replays commands and calculates metrics', () => { - const result = createSolveResult(definition, commands); - assert.deepEqual(result.finalState, [4, 4]); - assert.deepEqual(result.metrics, { - commands: 2, - divisions: 6, - plateSwitches: 1, - }); -}); - -test('formatConsoleResult renders grouped Russian commands', () => { - const result = createSolveResult(definition, commands); - assert.equal( - formatConsoleResult(result), - 'Найдено команд: 2\n' - + '1. Пластина 1 - влево x3\n' - + '2. Пластина 2 - вправо x3', - ); -}); - -test('already-open and unsolvable results have distinct text', () => { - const open = createSolveResult({ state: [4, 4], links: definition.links }, []); - const unsolvable = createSolveResult(definition, null); - assert.equal(formatConsoleResult(open), 'Замок уже открыт.'); - assert.equal(formatConsoleResult(unsolvable), 'Решение не найдено.'); - assert.deepEqual(unsolvable.commands, []); -}); - -test('solveLock validates and solves through the website-safe facade', () => { - const result = solveLock(definition); - assert.equal(result.status, 'solved'); - assert.deepEqual(result.finalState, [4, 4]); -}); -``` - -- [x] **Step 2: Подтвердить RED** - -Run: `node --test test/result.test.mjs` -Expected: FAIL с `ERR_MODULE_NOT_FOUND`. - -- [x] **Step 3: Реализовать построение результата через повторное применение команд** - -```js -// src/result.mjs -import { createTargetState } from './state-codec.mjs'; -import { applyCommand } from './transition.mjs'; - -export function createSolveResult(definition, commands) { - const initialState = [...definition.state]; - const targetState = createTargetState(initialState.length); - - if (commands === null) { - return { status: 'unsolvable', initialState, targetState, commands: [] }; - } - - let finalState = initialState; - for (const command of commands) { - const nextState = applyCommand(finalState, definition.links, command); - if (nextState === null) { - throw new Error('Решатель вернул заблокированную команду.'); - } - finalState = nextState; - } - - if (!finalState.every((position) => position === 4)) { - throw new Error('Решатель не привел замок к целевому состоянию.'); - } - - const plateSwitches = commands.reduce((count, command, index) => { - if (index === 0 || command.plate === commands[index - 1].plate) { - return count; - } - return count + 1; - }, 0); - - return { - status: 'solved', - initialState, - targetState, - commands: commands.map((command) => ({ ...command })), - finalState, - metrics: { - commands: commands.length, - divisions: commands.reduce((sum, command) => sum + command.steps, 0), - plateSwitches, - }, - }; -} - -export function formatConsoleResult(result) { - if (result.status === 'unsolvable') { - return 'Решение не найдено.'; - } - if (result.commands.length === 0) { - return 'Замок уже открыт.'; - } - - const directionNames = { left: 'влево', right: 'вправо' }; - const lines = result.commands.map((command, index) => ( - `${index + 1}. Пластина ${command.plate} - ${directionNames[command.direction]} x${command.steps}` - )); - return [`Найдено команд: ${result.commands.length}`, ...lines].join('\n'); -} -``` - -```js -// src/index.mjs -import { validateLockDefinition } from './lock-definition.mjs'; -import { createSolveResult } from './result.mjs'; -import { findShortestCommands } from './solver.mjs'; - -export function solveLock(value) { - const definition = validateLockDefinition(value); - return createSolveResult(definition, findShortestCommands(definition)); -} - -export { applyCommand, generateTransitions } from './transition.mjs'; -``` - -- [x] **Step 4: Подтвердить GREEN** - -Run: `pnpm test` -Expected: все тесты PASS. - -- [x] **Step 5: Commit** - -```bash -git add src/index.mjs src/result.mjs test/result.test.mjs -git commit -m "feat: format lock solving results" -``` - ---- - -### Task 6: CLI, консоль, создание и перезапись JSON-файла - -**Files:** -- Create: `src/cli.mjs` -- Create: `solve-lock.mjs` -- Create: `test/cli.test.mjs` -- Modify: `package.json` - -**Interfaces:** -- Produces: `parseArgs(argv): { inputPath: string, outputPath: string | null }`. -- Produces: `runCli(argv, streams?): Promise<0 | 1 | 2>`. -- `solve-lock.mjs` передает `process.argv.slice(2)` и устанавливает `process.exitCode`. -- При `--output` файл записывается до печати успешного результата; `writeFile` полностью перезаписывает существующий файл. - -- [x] **Step 1: Написать интеграционные тесты реального процесса Node.js** - -```js -// test/cli.test.mjs -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { spawnSync } from 'node:child_process'; -import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const projectRoot = dirname(dirname(fileURLToPath(import.meta.url))); -const entry = join(projectRoot, 'solve-lock.mjs'); - -async function createCase(t, definition) { - const directory = await mkdtemp(join(tmpdir(), 'gothic-lock-solver-')); - t.after(() => rm(directory, { recursive: true, force: true })); - const inputPath = join(directory, 'lock.json'); - await writeFile(inputPath, JSON.stringify(definition), 'utf8'); - return { directory, inputPath }; -} - -function run(args) { - return spawnSync(process.execPath, [entry, ...args], { encoding: 'utf8' }); -} - -test('prints a solution without creating an output file by default', async (t) => { - const { directory, inputPath } = await createCase(t, { - state: [1, 7], - links: [[0, 0], [0, 0]], - }); - const outputPath = join(directory, 'solution.json'); - const result = run([inputPath]); - assert.equal(result.status, 0); - assert.match(result.stdout, /Найдено команд: 2/); - assert.equal(result.stderr, ''); - await assert.rejects(readFile(outputPath, 'utf8'), { code: 'ENOENT' }); -}); - -test('--output creates and then overwrites a JSON result', async (t) => { - const { directory, inputPath } = await createCase(t, { - state: [1, 7], - links: [[0, 0], [0, 0]], - }); - const outputPath = join(directory, 'solution.json'); - const created = run([inputPath, '--output', outputPath]); - assert.equal(created.status, 0); - assert.equal(JSON.parse(await readFile(outputPath, 'utf8')).status, 'solved'); - - await writeFile(outputPath, 'stale'.repeat(1_000), 'utf8'); - const overwritten = run([inputPath, '--output', outputPath]); - assert.equal(overwritten.status, 0); - assert.match(overwritten.stdout, /Пластина 1 - влево x3/); - assert.equal(JSON.parse(await readFile(outputPath, 'utf8')).status, 'solved'); -}); - -test('invalid JSON exits with code 1 and a Russian error', async (t) => { - const { inputPath } = await createCase(t, { - state: [4, 4], - links: [[0, 0], [0, 0]], - }); - await writeFile(inputPath, '{', 'utf8'); - const result = run([inputPath]); - assert.equal(result.status, 1); - assert.match(result.stderr, /Ошибка: Некорректный JSON/); -}); - -test('an unsolvable lock writes JSON and exits with code 2', async (t) => { - const { directory, inputPath } = await createCase(t, { - state: [1, 7], - links: [[0, 1], [1, 0]], - }); - const outputPath = join(directory, 'solution.json'); - const result = run([inputPath, '--output', outputPath]); - assert.equal(result.status, 2); - assert.match(result.stdout, /Решение не найдено/); - assert.equal(JSON.parse(await readFile(outputPath, 'utf8')).status, 'unsolvable'); -}); - -test('read, write, and CLI grammar errors exit with code 1', async (t) => { - const { directory, inputPath } = await createCase(t, { - state: [4, 4], - links: [[0, 0], [0, 0]], - }); - - const missingInput = run([join(directory, 'missing.json')]); - assert.equal(missingInput.status, 1); - assert.match(missingInput.stderr, /Ошибка:/); - - for (const args of [ - [inputPath, '--unknown'], - [inputPath, '--output'], - [inputPath, 'extra'], - ]) { - const invalidArgs = run(args); - assert.equal(invalidArgs.status, 1); - assert.match(invalidArgs.stderr, /Ошибка: Использование:/); - } - - const writeError = run([inputPath, '--output', directory]); - assert.equal(writeError.status, 1); - assert.match(writeError.stderr, /Ошибка:/); -}); -``` - -- [x] **Step 2: Подтвердить RED** - -Run: `node --test test/cli.test.mjs` -Expected: FAIL, потому что `solve-lock.mjs` отсутствует. - -- [x] **Step 3: Реализовать CLI-оркестрацию и точные коды выхода** - -```js -// src/cli.mjs -import { readFile, writeFile } from 'node:fs/promises'; -import { solveLock } from './index.mjs'; -import { parseLockDefinition } from './lock-definition.mjs'; -import { formatConsoleResult } from './result.mjs'; - -export function parseArgs(argv) { - if (argv.length === 1) { - return { inputPath: argv[0], outputPath: null }; - } - if (argv.length === 3 && argv[1] === '--output') { - return { inputPath: argv[0], outputPath: argv[2] }; - } - throw new Error('Использование: node solve-lock.mjs [--output ]'); -} - -export async function runCli(argv, streams = {}) { - const stdout = streams.stdout ?? process.stdout; - const stderr = streams.stderr ?? process.stderr; - - try { - const { inputPath, outputPath } = parseArgs(argv); - const definition = parseLockDefinition(await readFile(inputPath, 'utf8')); - const result = solveLock(definition); - - if (outputPath !== null) { - await writeFile(outputPath, `${JSON.stringify(result, null, 2)}\n`, 'utf8'); - } - stdout.write(`${formatConsoleResult(result)}\n`); - return result.status === 'unsolvable' ? 2 : 0; - } catch (error) { - stderr.write(`Ошибка: ${error.message}\n`); - return 1; - } -} -``` - -```js -#!/usr/bin/env node -// solve-lock.mjs -import { runCli } from './src/cli.mjs'; - -process.exitCode = await runCli(process.argv.slice(2)); -``` - -Replace `package.json` with the complete final content: - -```json -{ - "name": "gothic-lock-solver", - "version": "0.1.0", - "private": true, - "type": "module", - "engines": { - "node": ">=20" - }, - "scripts": { - "test": "node --test", - "solve": "node solve-lock.mjs" - }, - "bin": { - "gothic-lock-solver": "./solve-lock.mjs" - } -} -``` - -- [x] **Step 4: Сделать точку входа исполняемой и подтвердить GREEN** - -Run: - -```bash -chmod +x solve-lock.mjs -pnpm test -``` - -Expected: все модульные и CLI-тесты PASS. - -- [x] **Step 5: Отдельно проверить ошибки аргументов** - -Run: - -```bash -node solve-lock.mjs -``` - -Expected: stderr начинается с `Ошибка: Использование:`, код выхода `1`. - -- [x] **Step 6: Commit** - -```bash -git add package.json solve-lock.mjs src/cli.mjs test/cli.test.mjs -git commit -m "feat: add Gothic lock solver CLI" -``` - ---- - -### Task 7: Сквозной пример и измерение предельного случая - -**Files:** -- Create: `examples/lock.example.json` -- Create: `test/e2e.test.mjs` -- Create: `test/differential.test.mjs` -- Create: `scripts/benchmark.mjs` - -**Interfaces:** -- Consumes: готовый CLI и внутреннюю тестируемую `findShortestCommands`; стабильным внешним facade остается `solveLock` из `src/index.mjs`. -- Produces: воспроизводимый пяти-пластинчатый пример и ручной benchmark без жесткого временного порога. - -- [x] **Step 1: Сначала создать только падающий сквозной тест** - -Run: - -```bash -mkdir -p examples scripts -``` - -Expected: каталоги существуют, но `examples/lock.example.json` еще отсутствует, поэтому следующий тест действительно будет RED. - -```js -// test/e2e.test.mjs -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { spawnSync } from 'node:child_process'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const projectRoot = dirname(dirname(fileURLToPath(import.meta.url))); - -test('example lock produces the documented four-command solution', () => { - const result = spawnSync( - process.execPath, - [join(projectRoot, 'solve-lock.mjs'), join(projectRoot, 'examples/lock.example.json')], - { encoding: 'utf8' }, - ); - assert.equal(result.status, 0); - assert.equal( - result.stdout, - 'Найдено команд: 4\n' - + '1. Пластина 1 - влево x3\n' - + '2. Пластина 2 - вправо x3\n' - + '3. Пластина 4 - влево x2\n' - + '4. Пластина 5 - вправо x2\n', - ); -}); -``` - -- [x] **Step 2: Подтвердить RED до создания example-файла** - -Run: `node --test test/e2e.test.mjs` -Expected: FAIL из-за отсутствующего `examples/lock.example.json`. - -- [x] **Step 3: Добавить example-файл и получить GREEN** - -```json -{ - "state": [1, 7, 4, 2, 6], - "links": [ - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0] - ] -} -``` - -Run: `pnpm test` -Expected: все тесты PASS, включая точный четырехкомандный вывод. - -- [x] **Step 4: Добавить дифференциальную проверку BFS для всех двухпластинчатых состояний и матриц** - -```js -// test/differential.test.mjs -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { createTargetState } from '../src/state-codec.mjs'; -import { findShortestCommands } from '../src/solver.mjs'; -import { generateTransitions } from '../src/transition.mjs'; - -function referenceBfs(definition) { - const targetKey = createTargetState(definition.state.length).join(','); - const startKey = definition.state.join(','); - if (startKey === targetKey) { - return []; - } - - const queue = [{ state: definition.state, commands: [] }]; - const visited = new Set([startKey]); - for (let head = 0; head < queue.length; head += 1) { - const current = queue[head]; - for (const transition of generateTransitions(current.state, definition.links)) { - const key = transition.state.join(','); - if (visited.has(key)) { - continue; - } - const commands = [...current.commands, transition.command]; - if (key === targetKey) { - return commands; - } - visited.add(key); - queue.push({ state: transition.state, commands }); - } - } - return null; -} - -test('typed-array BFS matches reference BFS for every N=2 lock', () => { - for (const link12 of [-1, 0, 1]) { - for (const link21 of [-1, 0, 1]) { - const links = [[0, link12], [link21, 0]]; - for (let first = 1; first <= 7; first += 1) { - for (let second = 1; second <= 7; second += 1) { - const definition = { state: [first, second], links }; - assert.deepEqual( - findShortestCommands(definition), - referenceBfs(definition), - `state=${first},${second}; links=${link12},${link21}`, - ); - } - } - } - } -}); -``` - -Run: `node --test test/differential.test.mjs` -Expected: PASS для всех `9 * 49 = 441` конфигураций. - -- [x] **Step 5: Создать benchmark максимального пространства без performance-assertion** - -```js -// scripts/benchmark.mjs -import { performance } from 'node:perf_hooks'; -import { findShortestCommands } from '../src/solver.mjs'; - -const plateCount = 7; -const definition = { - state: Array.from({ length: plateCount }, () => 1), - links: Array.from( - { length: plateCount }, - () => Array.from({ length: plateCount }, () => 0), - ), -}; - -const startedAt = performance.now(); -const commands = findShortestCommands(definition); -const elapsedMs = performance.now() - startedAt; - -console.log(JSON.stringify({ - elapsedMs: Math.round(elapsedMs), - commands: commands?.length ?? null, - rssMiB: Math.round(process.memoryUsage().rss / 1024 / 1024), -}, null, 2)); - -if (commands?.length !== 7) { - process.exitCode = 1; -} -``` - -- [x] **Step 6: Выполнить функциональную и предельную проверку** - -Run: - -```bash -pnpm test -node scripts/benchmark.mjs -``` - -Expected: тесты PASS; benchmark завершается с кодом `0`, сообщает `commands: 7` и фактические `elapsedMs`/`rssMiB`. Не менять алгоритм только из-за субъективной оценки времени; сначала зафиксировать измерение. - -- [x] **Step 7: Commit** - -```bash -git add examples/lock.example.json test/e2e.test.mjs test/differential.test.mjs scripts/benchmark.mjs -git commit -m "test: verify solver end to end" -``` - ---- - -### Task 8: Проектный skill для интерактивного решения замков - -**Files:** -- Create: `.codex/skills/gothic-lock-solver/SKILL.md` -- Create: `.codex/skills/gothic-lock-solver/agents/openai.yaml` - -**Interfaces:** -- Consumes: наблюдаемое состояние и подтвержденные пользователем направленные связи. -- Produces: полный `lock.json`, запуск `solve-lock.mjs` и сгруппированную комбинацию. -- Depends on: стабильные интерфейсы CLI из Task 6 и проверенный пример из Task 7. - -- [x] **Step 1: Перед редактированием прочитать обязательные skill-инструкции** - -Read and follow полностью: - -```text -superpowers:writing-skills -skill-creator -``` - -Expected: дальнейшее создание следует их требованиям, а изменения skill будут включены в Git-коммит. - -- [x] **Step 2: Инициализировать проектный skill штатным генератором** - -Run: - -```bash -python3 /root/.codex/skills/.system/skill-creator/scripts/init_skill.py gothic-lock-solver --path .codex/skills -``` - -Expected: созданы `SKILL.md` и `agents/openai.yaml`; незаполненные scaffold-подсказки не остаются в финальном файле. - -- [x] **Step 3: Заменить `SKILL.md` точными рабочими инструкциями** - -````markdown ---- -name: gothic-lock-solver -description: Собирает модель пластинчатого замка Gothic 1 Remake и запускает проектный Node.js-решатель. Используй, когда пользователь сообщает позиции или связи пластин либо просит кратчайшую комбинацию; не применяй к обычным кодовым замкам и не угадывай неподтвержденные связи. ---- - -# Gothic Lock Solver - -Используй вычислительное ядро проекта, а не языковую модель, для поиска комбинации. - -## Правила модели - -- Пластин от 2 до 7, позиции каждой от 1 до 7, цель всегда 4. -- Влево означает `+1`, вправо означает `-1`. -- Каждая строка матрицы направленная и независимая; обратную связь не подразумевай. -- Связи не каскадируют. Диагональ матрицы равна 0. -- Если зависимая пластина не может выполнить весь сдвиг, команда заблокирована полностью. -- Не используй приложенный PDF в этом workflow. - -## Сбор входа - -1. Зафиксируй число пластин и текущий вектор слева направо. -2. Для каждой исходной пластины отдельно зафиксируй, какие другие пластины движутся вместе с ней. -3. Запиши `1` для того же направления, `-1` для противоположного, `0` только для подтвержденного отсутствия связи. -4. Не запускай решатель, пока в каждой строке не устранены неизвестные связи. -5. Создай JSON с полями `state` и `links` по схеме `docs/superpowers/specs/2026-08-31-gothic-lock-solver.md`. - -## Запуск - -Разреши корень проекта как три уровня вверх от каталога этого skill. Запусти: - -```bash -node /solve-lock.mjs -``` - -Добавляй `--output ` только когда пользователь просит файл результата. - -Передай пользователю команды без перегруппировки и с исходной нумерацией пластин: - -```text -Пластина K - влево|вправо xS -``` - -## Проверка в игре - -После каждой выполненной команды сверяй наблюдаемое состояние с рассчитанным, если пользователь сообщает его. При первом расхождении останови текущую комбинацию, зафиксируй фактический вектор и уточни только затронутую строку матрицы. Не продолжай по заведомо неверной модели. -```` - -- [x] **Step 4: Сгенерировать интерфейсные метаданные** - -Run: - -```bash -python3 /root/.codex/skills/.system/skill-creator/scripts/generate_openai_yaml.py .codex/skills/gothic-lock-solver --interface "display_name=Gothic Lock Solver" --interface "short_description=Собирает модель замка и находит кратчайшую комбинацию" --interface "default_prompt=Помоги описать и решить замок Gothic 1 Remake." -``` - -Expected: `agents/openai.yaml` описывает только этот skill и сохраняет автоматическое обнаружение. - -- [x] **Step 5: Валидировать структуру skill** - -Run: - -```bash -python3 /root/.codex/skills/.system/skill-creator/scripts/quick_validate.py .codex/skills/gothic-lock-solver -``` - -Expected: validation PASS, нет scaffold-placeholder и ошибок frontmatter. - -- [x] **Step 6: Выполнить независимый forward-test** - -Передать свежему subagent только skill и запрос: - -```text -Используй skill gothic-lock-solver из .codex/skills/gothic-lock-solver. Замок: две пластины, состояние [1, 7], связей нет. Получи комбинацию через проектный решатель. PDF не используй. -``` - -Expected: агент формирует нулевую матрицу `2x2`, запускает CLI и возвращает ровно две команды: пластина 1 влево `x3`, затем пластина 2 вправо `x3`. - -- [x] **Step 7: Зафиксировать skill в Git** - -```bash -git add .codex/skills/gothic-lock-solver -git commit -m "feat: add Gothic lock solving skill" -``` - ---- - -## Покрытие утвержденной спеки - -| Раздел спеки | Реализующая задача | -|---|---| -| §1 Цель, Node.js 20+, ESM, отсутствие зависимостей | Tasks 1 и 6 | -| §2 Состояние, направления и направленная матрица | Tasks 1 и 3 | -| §3 Атомарная команда `x1..x6`, блокировка, отсутствие каскада | Task 3 | -| §4 JSON и валидация | Tasks 1 и 6 | -| §5 Base-7 и BFS на типизированных массивах | Tasks 2, 4 и 7 | -| §5.1 Независимое ядро и CLI-адаптер | Tasks 1-6 | -| §6 Минимум макрокоманд и стабильный tie-break | Tasks 3, 4 и 7 | -| §7 CLI, overwrite и коды `0/1/2` | Task 6 | -| §8 JSON-результат, метрики и русский вывод | Tasks 5 и 6 | -| §9 Все обязательные сценарии | Tasks 1-7 | -| §10 Не-цели | Global Constraints | -| §11 Проектный skill | Task 8 | - ---- - -## Финальная проверка перед завершением реализации - -- [x] Прочитать и применить `superpowers:verification-before-completion`. -- [x] Run: `pnpm test` — Expected: весь набор PASS без пропусков. -- [x] Run: `node solve-lock.mjs examples/lock.example.json` — Expected: точные четыре сгруппированные команды из Task 7. -- [x] Run: `node scripts/benchmark.mjs` — Expected: код `0`, `commands: 7`, измерения напечатаны. -- [x] Run: `python3 /root/.codex/skills/.system/skill-creator/scripts/quick_validate.py .codex/skills/gothic-lock-solver` — Expected: PASS. -- [x] Run: `git status --short` — Expected: пустой вывод; все изменения сохранены коммитами. -- [x] Не заявлять о завершении, пока каждая команда выше не выполнена заново после последнего изменения. diff --git a/docs/superpowers/plans/2026-09-07-matrix-search.md b/docs/superpowers/plans/2026-09-07-matrix-search.md deleted file mode 100644 index 4f385f7..0000000 --- a/docs/superpowers/plans/2026-09-07-matrix-search.md +++ /dev/null @@ -1,64 +0,0 @@ -# Matrix search and benchmarks implementation plan - -> **For agentic workers:** use the task scope and interfaces below; verification evidence is mandatory. - -**Goal:** закрепить быстрый точный поиск минимума действий и воспроизводимое сравнение 45 замков в gothic-lock-solver. - -**Architecture:** браузерное ESM-ядро с точной матричной подготовкой, A* и BFS fallback. Node.js используется только для CLI, независимых проверок и создания отчёта. Матричный прототип из предыдущего исследования адаптируется к стабильному API и явным бюджетам ресурсов. - -**Tech Stack:** JavaScript ESM, BigInt, node:test; без runtime-зависимостей. - -**Spec:** ../specs/2026-09-07-matrix-search.md - -## Global Constraints - -- ES modules, Node.js 20+, без сторонних runtime-зависимостей. -- `solveLock(definition, {algorithm:'matrix-astar'|'bfs'}?)`; matrix-astar по умолчанию, прежний solved/unsolvable result. -- positions1..7, target4, directed non-cascading links, atomic blocking, each grouped command costs1. -- Вычислительное ядро не зависит от Node.js/DOM/сети. -- Исчерпание ресурсов даёт SearchLimitError, не unsolvable. -- Не менять .env, проектные skills, PDF или сторонний репозиторий. - -## Task 1: production search - -**Files:** src/matrix*.mjs, src/search*.mjs, src/solver.mjs, src/index.mjs, src/lock-definition.mjs; covering test files. Worker owns these files; result/CLI shape stays stable. - -**Interfaces:** public solveLock as above. Existing findShortestCommands remains exact BFS with previous stable command ordering; matrix path may choose another deterministic optimal sequence. Export SearchLimitError from facade. - -- [x] Add failing assertions for solveLock on independent N8; explicit BFS selection; invalid algorithm; resource exhaustion; no solution versus matrix false positive. Run red stage and record failures. -- [x] Implement exact matrix analysis, cheap legal scheduling certificate and matrix A*. Use a cheap greedy legal certificate first; bounded subset scheduling may follow, with safe mask representation. For singular matrices use bounded optimized macro BFS. -- [x] Preserve old BFS command order; precompute signed effects and base-7 offsets, calculate valid movement intervals, avoid per-edge state arrays. Validate budgets and safe encodings before allocation; bound frontier storage, not only expanded nodes. -- [x] Test the counterexample start[1,1,1] with all off-diagonal links−1 (unsolvable) and start[3,1,1] with the same links (4 actions). Test fractional unique solution and singular fallback. -- [x] Validate all 441 N2 cases and a deterministic bounded N3 sample against independent complete macro BFS; assert unit-click replay and no mutation of inputs. Existing tests remain green, except the deliberately updated model maximum and new documented mode differences. -- [x] Report files, tests, result, actual options/budget defaults, and concerns. No Git commit until root integrates independent files. - -## Task 2: catalog and evidence harness - -**Files:** benchmarks/**, scripts/benchmark.mjs, test/catalog.test.mjs, test/benchmark.test.mjs. Worker owns only these. Parent owns package.json and docs. - -**Interfaces:** call public solveLock(definition,{algorithm}); definition is {state,links}. Keep source fixtures under benchmarks/fixtures. Export a reusable independent unit-click replay helper for tests/harness. - -- [x] Import 45 entries from prior analysis catalog with provenance; preserve expected optimal counts and original competitor solutions/metrics. Record upstream SHA and data checksum. Add catalog tests against explicit BFS and default solver, including independent replay. -- [x] Preserve original Apache BFS baseline from commit a87e739a22ccc4215d6d9b14fa06b0738171cb60 with attribution and unchanged algorithm. Do not vendor AGPL solver code. -- [x] Implement deterministic benchmark CLI with repetitions, warmups, output directory and optional reference-repository path. Wrong comparator revision/hash must fail; no network required by ordinary tests. -- [x] Generate JSON and Markdown with per-lock A/U/C, min/median/max milliseconds, aggregates, runtime/source metadata and truthful comparisons. CLI writes report only after all correctness checks succeed. -- [x] Add focused tests for statistics/report generation/input validation and evidence failure handling; avoid wall-clock thresholds. Make smoke run reproducible without 45×slow timing in routine tests. -- [x] Report command and output format so parent can document/run final comparison. No Git commit. - -## Task 3: browser verification, docs and integration - -**Files:** README.md, AGENTS.md, package.json, docs/benchmarks/**, browser verification scripts/tests; parent owns these. - -- [x] Document algorithm choice, exact objective, changed deterministic tie policy, resource errors, N>=2 versus implementation capacity, browser ESM and Worker usage. -- [x] Add commands for unit tests, benchmark and browser verification. Verify actual ESM dependency graph in a browser-like restricted environment; perform a real browser import when available. -- [x] Run full tests and one repeated comparison against pinned UnlockMyLoot checkout, preserving report in docs/benchmarks. Inspect every action metric and aggregate; no claims derived from stale historical timing. -- [x] Obtain independent code review with spec and full diff. Address material findings and rerun only covering checks before final full gate. -- [x] Commit and push the feature to user's GitHub repository; return reviewable branch/PR and measured outcomes. Keep upstream submission as a separate step after our project records the result. - -## Verification notes - -- Independent reviews of the production search and benchmark harness found no material defects. -- Added a reproducible classic-script distribution for the requested no-build upstream variant; source synchronization and isolated execution are tested. -- A real browser attempt could not load the workspace HTTP server (`net::ERR_BLOCKED_BY_CLIENT`). ESM and classic-script VM checks passed, but actual browser/Worker verification is not claimed. - -- Final own-project test gate: 113/113 passed. Published core tree matches the tested local tree exactly. Full45 benchmark at clean published commit `6b1cfbc13bcec68f609d45d5dc97504dd84f1760` passed; report stored in `docs/benchmarks/run-20260907T070258001Z-f78dff13`. diff --git a/docs/superpowers/plans/2026-09-07-simplify-library.md b/docs/superpowers/plans/2026-09-07-simplify-library.md new file mode 100644 index 0000000..ec2db04 --- /dev/null +++ b/docs/superpowers/plans/2026-09-07-simplify-library.md @@ -0,0 +1,161 @@ +# Simplify the library implementation plan + +> **For agentic workers:** Use `superpowers:subagent-driven-development` or `superpowers:executing-plans` to execute these tasks. Checkboxes record progress. The owner has approved execution. Keep working logs and review outputs in ignored `artifacts/`, never in tracked documentation. + +**Goal:** Simplify draft PR #2 into an autonomous TypeScript library with a documented optional solver configuration and CLI configuration file. + +**Architecture:** Production code lives in a flat `src/`; tests and benchmark code live in `tests/`; maintained documentation lives in `wiki/`. GitHub Actions stores generated evidence and publishes the already verified package. Compare only our candidate and our actual target revision or preceding release. + +**Tech stack:** TypeScript, Vite, Vitest, Playwright, Oxlint and pnpm. Recheck official current stable versions before changing dependencies; preserve strict compiler checks. + +**Spec:** The owner-approved decisions below are the specification for this change. They supersede the earlier two-argument-only contract, preserved JavaScript copies, tracked benchmark snapshots and historical documentation layout. No separate specification or review-report file is needed. + +## Approved boundaries + +- Continue the existing `feat/typescript-core-library` branch and draft PR #2, targeting `feat/matrix-astar-benchmarks`. Preserve the reference branch and Git history. Do not merge, publish npm or create a release in this task. +- No references, adapters, copied descriptions or comparisons to other solver projects in the current tracked tree or PR description. Existing approved build tools, official documentation and own npm/CDN/repository URLs remain valid dependencies. +- No tracked `.js`, `.mjs`, `.mts` or generated distribution files. Executable source and configuration use `.ts`; browser fixture HTML may load generated JavaScript. Verify generated bytes without adding a runtime transpiler to integration examples. +- No tracked benchmark snapshots, timing/RSS samples, raw reports, coverage, traces, reviewer reports or copied old implementations. Fixed inputs and mathematical expected values remain test data. +- Node.js 22/24/26 and real Chromium/Firefox/WebKit remain required consumers. Coverage of shipped source requires at least 80% each of statements, branches, functions and lines. +- Keep strict TypeScript, checked indexed access, exact optional properties and `skipLibCheck: false`. No `any`, suppression-based workaround, hidden dependency patch or silent version downgrade. +- Local tests, build, package verification and benchmarks finish before workflow edits. Independent review follows integration; fix findings before updating the PR. + +## Target files and ownership + +| Location | Responsibility | +| --- | --- | +| `src/index.ts` | Public solver facade, named exports and tuple-aware signature | +| `src/cli.ts` | Import-safe CLI entry, input/config/output IO and Russian messages | +| `src/types.ts` | State, links, positions and commands | +| `src/errors.ts` | Invalid-input and resource-exhaustion errors | +| `src/config.ts` | `SolverConfig`, defaults, shared validation and `createSolverConfig` | +| `src/lock.ts` | Validated model, direct effects and safe state representation | +| `src/matrix.ts` | Exact rational analysis | +| `src/astar.ts` | Certificate, matrix-guided exact search and search budget handling | +| `src/bfs.ts` | Exact singular-case search using the same configuration | +| `src/priority-queue.ts` | Indexed priority queue needed by A* | +| `tests/unit/` | Domain, config, CLI, oracle and automation behavior checks | +| `tests/e2e/` | Real browser, Worker, CLI and installed-package consumers, including type consumers | +| `tests/benchmarks/` | Controlled comparison, independent replay, RSS workers, snapshot generation and `fixtures.json` | +| `wiki/` | `api.md`, `configuration.md`, `algorithm.md`, `development.md`, `benchmarks.md`, `releasing.md` | +| `.github/workflows/` | CI and release workflows | +| `.github/scripts/` | Required build/release coordination commands only | +| Root | README, AGENTS, LICENSE, package/lockfile, Git and tool configuration | +| `docs/superpowers/plans/` | This one current plan; no accumulated historical specifications or reports | + +Ignored `dist/` contains five generated runtime choices: readable/minified ESM, readable/minified classic script and standalone Node CLI, plus declarations/checksums. Ignored `artifacts/` contains local results; CI uploads the corresponding outputs. No source changes are generated back into Git during CI. + +## Task 1 — Prove the unpatched toolchain + +**Files:** `package.json`, `pnpm-lock.yaml`, `pnpm-workspace.yaml`, `vitest.config.ts`, TypeScript configs, `patches/`, `.gitattributes`. + +- [x] Record current official stable versions and relevant supported Node ranges from the providers. Use exact resolved versions and lockfile integrity. +- [x] In a disposable install, reproduce strict checking with the Vitest patch removed. Capture the actual declaration errors before selecting a fix. +- [x] Resolve through a supported published version or documented normal configuration/dependency setup. Remove the patch and patch-only workspace/Git settings once this passes. +- [x] Run frozen install, lint, typecheck and a Node/browser Vitest smoke test. Do not reinterpret a failing type check as success. + +Commands: `pnpm install --frozen-lockfile`, `pnpm check`, `pnpm test:node`, then one real-browser project. If the current published toolchain cannot satisfy the agreed strictness without patching, report that concrete incompatibility before proceeding with a substitute or relaxed requirement. This is the first technical gate, not an item deferred until CI. + +## Task 2 — Flatten the core and expose configuration + +**Files:** `src/`, `tests/unit/core*.test.ts`, new `tests/unit/config.test.ts`. + +**Interface:** `createSolverConfig(overrides?: Partial): SolverConfig`; `solveLock(state, links, config?: SolverConfig): readonly Command[] | null`. Preserve the current generic state/link dimension checks. Export the factory and `SolverConfig` through `index.ts`, including the browser namespace build. + +| Config field | Default | Accepted values | +| --- | ---: | --- | +| `maxVisited` | 2,000,000 | Positive safe integer | +| `maxExpanded` | 1,000,000 | Nonnegative safe integer; zero permits no state expansion | +| `maxFrontier` | 1,000,000 | Positive safe integer | +| `maxDenseBytes` | 67,108,864 | Nonnegative safe integer; zero selects sparse storage | + +- [x] Add failing public tests for defaults, partial overrides, freezing, nonmutation, unknown keys, nonnumeric/fractional/unsafe values and invalid complete configs supplied from JavaScript. +- [x] Add public resource-limit tests covering A*, singular BFS, dense/sparse behavior and the certificate path. For example: + +```ts +const config = createSolverConfig({ maxExpanded: 0 }); +expect(Object.isFrozen(config)).toBe(true); +expect(() => solveLock( + [3, 1, 1], + [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]], + config, +)).toThrow(SearchLimitError); +``` + +- [x] Watch each new behavior fail, then implement defaults and shared validation in `config.ts`. The factory accepts partial overrides; the solver accepts the complete readonly config and revalidates ordinary JS input. Missing config uses defaults. Invalid config uses `LockInputError`; exhaustion uses `SearchLimitError`. +- [x] Move/merge the current lock model and prepared state into `lock.ts`; rational analysis into `matrix.ts`; matrix search into `astar.ts`; BFS into `bfs.ts`; heap into `priority-queue.ts`. Fold tiny checked-access helpers and shared budget operations into the modules that own them; do not duplicate budget rules. +- [x] Preserve positions 1–7, target 4, direct noncascading links, zero-based signed commands, exact arithmetic and minimum grouped actions. Configurable budgets do not remove safe-number/representation limits or turn exhaustion into `null`. +- [x] Run unit/oracle tests and strict checking. Compare default-config results with the prior default behavior on every fixed input. + +## Task 3 — Unify CLI and built consumers + +**Files:** old `cli/`, root `solve-lock.mjs`, `src/cli.ts`, `tests/unit/cli.test.ts`, `tests/e2e/`, build/consumer scripts and root Vite/Vitest/Playwright/TypeScript/package configs. + +- [x] First add failing CLI tests for `--config`, omitted/default settings, partial JSON, invalid JSON, unknown fields, missing files and duplicate/unknown flags. +- [x] Consolidate CLI into `src/cli.ts`; use the shared factory and solver. Importing the file in tests must not start the CLI. Preserve standalone execution, npm bin, Russian output, optional `--output`, and exits 0/2/1 for solved/unreachable/error. + +```sh +node dist/gothic-lock-solver.cli.mjs lock.json --config solver.config.json +node dist/gothic-lock-solver.cli.mjs lock.json --config solver.config.json --output solution.json +node dist/gothic-lock-solver.cli.mjs --help +``` + +- [x] Delete the root JavaScript adapter and old `cli/`. Move package/distribution checks into `tests/e2e/`; convert `.mts` type fixtures to `.ts`. +- [x] Replace tracked worker/module `.mjs` fixtures with TS fixture sources compiled to ignored test output through Vite. Keep assertions against the exact generated library bytes in HTTP, classic, module, Worker and file scenarios. +- [x] Move necessary build coordination into `.github/scripts/`; move package verification into `tests/e2e/`. Update scripts, relative URLs, static server paths, test includes and declaration exports together. +- [x] Verify the factory in both ESM files and both classic globals. Add installed positive/negative type-consumer checks for optional config without weakening existing tuple checks. +- [x] Build all five runtime files; run distribution, browser and package/CLI consumers in Node 22/24/26. Measure coverage over all shipped `src/**/*.ts`, including unimported entries. + +## Task 4 — Make benchmarks autonomous and artifact-only + +**Files:** old `benchmarks/`, old `scripts/benchmark*.ts`, `tests/unit/benchmark.test.ts`, `tests/unit/core-oracle.test.ts`, `tests/benchmarks/`, `.gitignore`. + +- [x] Review the existing uncommitted cleanup; verify all 45 numerical state/link configurations and mathematical minima match the prior corpus. Preserve neutral IDs. Do not retain copied descriptions, links, encoded sharing formats or external solution paths. +- [x] Consolidate fixed inputs and expected minimum-action assertions into `tests/benchmarks/fixtures.json`. These are test expectations, not a timing snapshot. Remove redundant manifests, historical commands and committed result snapshots. +- [x] Move measurement/replay/RSS/snapshot code into `tests/benchmarks/`. Remove both vendored implementation trees and the optional original-BFS comparison slot. Retain the independent small-state oracle in unit tests. +- [x] Require an explicit own baseline module for matched comparisons. Generate its snapshot into ignored output by executing that module on the current fixtures. CI supplies the actual PR target checkout; releases supply verified preceding-release bytes. The first release uses the preserved own reference revision. +- [x] Cover missing/mismatched baseline identity, bad paths, worse action/plate/shift metrics, unknown options, source tampering and evidence overwrite in focused tests. Never generate expected baseline quality from the candidate. +- [x] Run candidate and baseline in the same Node environment, alternating order with unchanged-control calibration. Keep timing and memory uncertainty explicit and preserve bounded-repeat observations. Default-config results must retain all 45 known minima. + +Planned local interface (after a separate own baseline checkout is prepared): + +```sh +pnpm benchmark --baseline-module ../gothic-lock-solver-base/src/index.mjs --baseline-format legacy --output-dir artifacts/benchmark +``` + +The legacy path belongs to the preserved separate checkout, not this tracked source tree. For a typed target, build that checkout and pass its `dist/gothic-lock-solver.mjs` with format `tuple`. A single command generates baseline snapshot plus comparison outputs under the selected ignored output directory. Full JSON, Markdown, snapshots and memory samples become CI artifacts and release attachments; no report is copied into Git. + +## Task 5 — Replace documentation with maintained Wiki pages + +**Files:** `README.md`, `AGENTS.md`, `wiki/`, old `docs/`, root configuration references. + +- [x] Move the useful API, mathematical proof/complexity, development, benchmark and release documentation into the six focused Wiki pages listed above. Add the config defaults/ranges/units, factory examples, JS validation, resource behavior and CLI JSON example to `wiki/configuration.md`. +- [x] Keep README to purpose, minimal API/CLI/CDN examples, verification commands and Wiki links. Keep AGENTS to current architecture, invariants and commands; it links the same maintained pages. +- [x] Delete historical specifications, duplicated plans, reviewer reports, benchmark directories and integration proposals. Retain only this active Superpowers plan under `docs/`. +- [x] Check local Markdown links and scan current tracked content for other solver names/URLs/adapters. References to standard tool documentation are permitted; project marketing and examples are self-contained. +- [x] Confirm tracked root directories are only `src`, `tests`, `wiki`, `.github` and the optional active `docs` plan. Remove patch-only and obsolete root helpers/configuration. + +## Task 6 — Run the complete local gate, then adapt workflows + +**Files:** `.github/scripts/`, `.github/workflows/`, automation tests under `tests/unit/`. + +- [x] Before any workflow edits, execute frozen install, `pnpm test` and the full autonomous 45-input benchmark. Verify the same archive in Node 22/24/26 and real Chromium/Firefox/WebKit. Store logs in ignored output. +- [x] Update workflow paths and benchmark inputs to the verified commands. Remove all other-solver checkouts. Keep actual PR-base isolation, official SHA-pinned Actions, read-only candidate execution and separate privileged publication stages. +- [x] Upload test/coverage/browser/benchmark evidence from ignored outputs. Store snapshots only as CI artifacts or GitHub Release assets. Release comparison uses immutable verified baseline bytes and identity checks. +- [x] Preserve npm OIDC readiness, exact archive delivery, CDN verification, recovery and explicit treatment of uncertain performance. No publishing in this task. +- [x] Validate YAML/action pins/shell syntax and run changed automation tests. Independent review checks the agreed layout, config/CLI semantics, all-45 quality, artifact provenance and absence of tracked generated evidence. Review output stays outside Git. + +## Task 7 — Update the existing draft PR and verify hosted CI + +- [ ] Inspect the complete diff, deleted/moved files and working-tree state. Commit only source, tests, maintained Wiki, config and this active plan; never commit generated outputs. +- [ ] Push the existing branch without changing its base or rewriting preserved history. Replace PR #2 description with the simplified structure, public config/CLI contract and self-contained reproduction commands. Remove prior cross-project comparisons and obsolete evidence links. +- [ ] Run GitHub CI and inspect each required job, including the actual target comparison. Fix concrete failures; do not rerun benchmarks to select a preferred performance result. +- [ ] Link fresh CI artifacts from the PR and report exact observed test/coverage/quality/performance outcomes. Keep the PR draft. No merge, npm publication or release. + +## Completion criteria + +The current tree matches the agreed layout; all executable sources are TS; no other solver projects, copied implementations, dependency patch or generated reports remain tracked. Factory/default/custom config and CLI config are tested and documented. All five builds and required consumers pass, all 45 inputs retain mathematical correctness, and uncertainty is honestly reported. The same draft PR contains fresh hosted verification and artifact links. + +## Planning status + +The owner approved execution through subagents after confirming the two build entry points. Implementation follows this plan; generated verification evidence remains outside Git. diff --git a/docs/superpowers/plans/2026-09-07-typescript-library.md b/docs/superpowers/plans/2026-09-07-typescript-library.md deleted file mode 100644 index b645aa6..0000000 --- a/docs/superpowers/plans/2026-09-07-typescript-library.md +++ /dev/null @@ -1,108 +0,0 @@ -# Gothic Lock Solver TypeScript Library Implementation Plan - -> **For agentic workers:** use superpowers:subagent-driven-development; follow this plan task by task with independent review. - -**Goal:** Выпустить проверяемую TypeScript-библиотеку с четырьмя самодостаточными форматами, доказательными сравнениями и подготовленным npm/Release workflow. - -**Architecture:** Один именованный facade solveLock делегирует проверенной модели и точным стратегиям поиска. Инструменты сборки, независимый oracle и измерения находятся вне runtime. npm и Release получают байты одного проверенного tarball; jsDelivr раздаёт файлы npm. - -**Tech Stack:** TypeScript 7.0.2, Vite 8.2.2, Vitest 5.0.0, Playwright 1.63.0, Oxlint 1.81.0, pnpm 12.3.4; актуальность перепроверена через npm registry. - -**Spec:** `docs/superpowers/specs/2026-09-07-typescript-library-design.md`. - -## Global Constraints - -- Только наш репозиторий, ветка feat/typescript-core-library; main и референс не меняются. -- Публичный API: `solveLock(state, links): readonly Command[] | null`, без default export и параметров настройки. -- Position=1..7, N>=2, goal=4; links[source][target]=-1/0/1, диагональ 0. Command=[zero-based index, signed numeric pin delta], delta!=0. -- Минимум действий A обязателен; U и C измеряются отдельно. Null означает доказанную недостижимость, не исчерпание ресурсов. -- Strict TypeScript, noUncheckedIndexedAccess, exactOptionalPropertyTypes и остальные флаги спецификации; без any, ts-ignore и необоснованных assertions. -- Ровно четыре независимых runtime-файла: gothic-lock-solver.mjs, gothic-lock-solver.min.mjs, gothic-lock-solver.js, gothic-lock-solver.min.js. IIFE global GothicLockSolver, ноль runtime-зависимостей. -- Node 24/26 и настоящие Chromium/Firefox/WebKit; обязательные среды не заменяются VM/jsdom. -- 45 исходных fixtures сохраняют hashes/provenance. Сторонний код AGPL не включается в пакет и репозиторий; подключается из закреплённого checkout. -- Метрики и baseline не перезаписываются автоматически; время/память с неопределённостью, без выдуманной точности. -- Только официальные Actions latest stable с SHA, официальный npm CLI/OIDC и gh; публичный npm и бесплатный CDN. - -## Ownership and interfaces - -| Task | Owns | Consumes / produces | -|---|---|---| -| 1 build and environment | package.json, lockfile, tsconfig*, vite/vitest/playwright config, .oxlintrc*, scripts/build*, scripts/verify-package*, tests/distribution*, tests/e2e*, tests/browser* | Consumes src/index.ts; produces dist four files plus declarations, package.tgz and `pnpm check`, `pnpm test:node`, `pnpm test:browser`, `pnpm test:e2e`, `pnpm build`, `pnpm verify:package` | -| 2 mathematical core | src/**/*.ts, tests/unit/core*, tests/helpers/oracle*, tests/types* | Produces named solveLock/types/errors; tests do not require bundle build | -| 3 evidence | benchmarks/**/*.ts, benchmarks/snapshots/*, tests/unit/benchmark*, scripts/benchmark*, benchmarks/README.md | Consumes public solveLock and pinned reference; produces `pnpm benchmark --reference PATH --output-dir PATH`, `pnpm benchmark:compare --baseline-module PATH --baseline-snapshot PATH --reference PATH --output-dir PATH` | -| 4 integration and delivery | .github/**, README.md, AGENTS.md, cli/**, solve-lock.mjs, docs/**, obsolete-file cleanup | Consumes all above commands; preserves repo CLI, prepares release/canary, produces draft PR and measured evidence | - -Task 1 and Task 2 share an interface, not source files; installing tooling may precede core tests. Task 3 starts on preserved fixtures and reference while Tasks 1/2 proceed. Main coordinator owns integration and removes obsolete production code only after equivalence checks. - -## Task 1: Build and verified consumers - -- [ ] Configure exact devDependencies and strict compiler options; install with pnpm and save lockfile. No optional lint/typecheck bypass. -- [ ] Write distribution tests against actual outputs: import both .mjs in Node; execute both classic files in browsers; exercise Worker and isolated HTML from a static server. -- [ ] Observe missing-artifact failure before implementing build. Configure Vite library ESM/IIFE twice for readable/minified outputs; verify actual minification and no imports/chunks/runtime globals requiring Node. -- [ ] Type declarations accompany package exports; no default export. Test negative type imports and clean installed tarball outside repo resolution. -- [ ] Vitest projects run shared core/catalog cases in Node and Chromium/Firefox/WebKit. Playwright E2E imports static bytes without Vite transformations. -- [ ] Complete build/package tests and report exact commands, engines and limitations. - -Expected consumer behavior: - -```ts -import { solveLock } from 'gothic-lock-solver'; -import * as GothicLockSolver from 'gothic-lock-solver'; -expect(solveLock([6, 2], [[0, -1], [0, 0]])).toEqual([[0, -2]]); -expect(GothicLockSolver.solveLock([4, 4], [[0, 0], [0, 0]])).toEqual([]); -``` - -## Task 2: Exact typed core - -- [ ] Write new tuple-contract behavior tests before the new entry exists. Test asymmetric direction/sign, no cascade, immutability, open/unsolvable, invalid sparse input and independent action optimality. -- [ ] Port the already proven matrix algorithm into typed single-responsibility model, exact rational functions, indexed heap, budget, A* and BFS. Preserve command order and resource limits; the old object API remains only in pinned reference code. -- [ ] Enforce compile-time tuple dimension checks where known, plus dynamic runtime validation. Document exactly which assertions are logically justified; no unverified non-null indexing. -- [ ] Differential-test all 441 N2 cases, seeded N3, singular/deadlocked/fractional matrices, N8 and safe wider state codes, limits separately from null. -- [ ] Run all 45 catalog cases with independent replay and expected minimum A. Apply each intermediate unit shift in oracle replay. -- [ ] Run typecheck and focused tests; provide self-review of mathematical invariants and time/memory impact. - -Representative red tests: - -```ts -expect(solveLock([6, 2], [[0, -1], [0, 0]])).toEqual([[0, -2]]); -expect(solveLock([1, 1, 1], [[0,-1,-1],[-1,0,-1],[-1,-1,0]])).toBeNull(); -expect(() => Reflect.apply(solveLock, undefined, [[0, 4], [[0,0],[0,0]]])).toThrow(); -``` - -## Task 3: Reproducible benchmark evidence - -- [ ] Preserve fixture hashes and isolate immutable reference matrix source from commit 6b1cfbc13bcec68f609d45d5dc97504dd84f1760 (or byte-identical current reference). Keep original Apache BFS and upstream pin. -- [ ] Write tests for independent tuple replay, metrics A/U/C/switches, compare regression/inconclusive, source tampering and baseline non-overwrite before implementing new harness. -- [ ] Produce stable quality snapshot for all 45 locks and compare against target branch snapshot. Store optimum A and candidate result metrics separately from historic upstream counts. -- [ ] Measure warmups/repetitions, median/p95/raw samples; alternate candidate/base in one environment. Calibrate unchanged-vs-itself before a performance verdict. Paired comparison must not rely on historical times from another machine. -- [ ] Measure process peak RSS in isolated workers separately from timing; record baseline/runtime cost and browser-memory unavailable explicitly. -- [ ] Report environment/source/fixture hashes, compared SHAs, bundle bytes, per-lock differences and full JSON/Markdown. Upstream adapter verifies pinned file/block hashes before executing external code. -- [ ] Add meaningful Vitest bench entry for developer runs; release verdict uses controlled comparison rather than a noisy single benchmark threshold. -- [ ] Run reference + candidate on all 45; report improvements and regressions honestly, fix algorithmic regressions rather than relaxing snapshot. - -Comparison test semantics: - -```ts -// A regression is always a deterministic failure; timing uncertainty cannot hide it. -expect(compareMetrics({ A: 4, U: 2, C: 8 }, { A: 3, U: 2, C: 8 }).quality).toBe('regression'); -// Historical timings alone never prove a matched-environment performance pass. -``` - -## Task 4: Integrate, document and deliver - -- [ ] Adapt repo CLI to invoke typed facade, preserving Russian terminal messages and explicit output file behavior; test CLI against actual built core. -- [ ] Prepare official pinned GitHub Actions CI: frozen install, lint/typecheck, Node/browser/package/E2E, pinned upstream checkout and base-branch comparison; upload visible evidence. -- [ ] Prepare main stable and optional manually invoked canary workflow: same checked tarball -> npm OIDC -> registry/CDN verification -> GitHub Release. Fail distinctly on partial publish; never overwrite npm versions or source evidence. -- [ ] Document package/bootstrap ownership, exports and all examples, exact mathematical guarantee and complexity, resource errors, benchmark interpretation, source/style conventions and reviewer commands. -- [ ] Remove superseded handwritten bundler and duplicate active .mjs core only after replacement passes; preserve original historical docs and pinned reference implementations. -- [ ] Run full `pnpm check`, package consumer tests, full benchmark compare, and independent whole-branch review. Fix material findings and recheck affected behavior. -- [ ] Publish source branch and generated evidence, create draft PR against feat/matrix-astar-benchmarks. Do not merge main, publish npm, create release, or modify upstream repository. - -## Verification record - -Before migration: `pnpm test` passes 113/113 tests, no failures/skips; Node 24.19.0. -Latest registry versions were checked before tooling installation. Main remains a87e739; reference branch remains 3cbbeed. - -## Accepted execution refinements (2026-09-07) - -The owner added a fifth Vite-built self-contained Node CLI (`gothic-lock-solver.cli.mjs`) with npm bin, maintained Node22 consumer support, strict >=80% statements/branches/functions/lines across src+cli, and English README/focused engineering docs. The four library formats and two-argument tuple API remain as specified. An independent reviewer reads the owner's public-safe principles and current official TypeScript7 documentation, reviews code and real behavior evidence. Workflows are the final step after all local checks and benchmarks; no .github implementation starts before local acceptance. All changes belong to one draft PR from the preserved reference branch. diff --git a/docs/superpowers/specs/2026-08-31-gothic-lock-solver.md b/docs/superpowers/specs/2026-08-31-gothic-lock-solver.md deleted file mode 100644 index 77fb13a..0000000 --- a/docs/superpowers/specs/2026-08-31-gothic-lock-solver.md +++ /dev/null @@ -1,360 +0,0 @@ -# Решатель замков Gothic 1 Remake - -## Спецификация 1.0 - -Дата: 2026-08-31 -Статус: утверждена пользователем и реализована. - -Историческая спецификация версии 1.0. Изменения алгоритма, размера модели, равных решений и доказательного набора описаны в [спецификации от 2026-09-07](2026-09-07-matrix-search.md), которая имеет приоритет для новой версии. - -## 1. Цель - -Создать детерминированный решатель замков Gothic 1 Remake без использования искусственного интеллекта. - -Первая версия - CLI-утилита на Node.js. Она получает описание замка из JSON-файла, ищет выигрышную комбинацию и по умолчанию выводит ее в консоль. По флагу результат записывается в JSON-файл. - -Технические ограничения первой версии: - -- обычный JavaScript в формате ES modules (`.mjs`); -- Node.js 20 или новее; -- отсутствие сторонних runtime-зависимостей; -- тесты на встроенном `node:test`. - -В дальнейшем вычислительное ядро должно быть пригодно для использования на сайте. - -## 2. Модель замка - -### 2.1. Пластины и позиции - -- Количество пластин `N`: от 2 до 7. -- Каждая пластина имеет ровно 7 положений: от `1` до `7`. -- `1` - крайнее левое положение. -- `7` - крайнее правое положение. -- Целевое положение каждой пластины - `4`. -- Состояние замка - упорядоченный вектор длины `N`. - -Пример состояния пяти пластин: - -```json -[7, 4, 1, 3, 6] -``` - -Целевое состояние: - -```json -[4, 4, 4, 4, 4] -``` - -### 2.2. Направления - -- Движение влево увеличивает номер позиции: `+1`. -- Движение вправо уменьшает номер позиции: `-1`. - -Например, пластина в позиции `3` после одного деления влево окажется в позиции `4`. - -### 2.3. Матрица связей - -Связи задаются направленной квадратной матрицей `N x N`. - -Допустимые значения: - -- `0` - связи нет; -- `1` - зависимая пластина движется в том же направлении; -- `-1` - зависимая пластина движется в противоположном направлении. - -Строка `i` описывает только эффект ручного движения пластины `i` на остальные пластины. - -Матрица не обязана быть симметричной. Связь `i -> j` не означает наличие связи `j -> i`. - -Диагональ всегда равна нулю: - -```text -links[i][i] = 0 -``` - -Движение выбранной пластины учитывается отдельно и не кодируется связью с самой собой. - -Пример: - -```json -[ - [0, 1, -1], - [0, 0, 0], - [1, 0, 0] -] -``` - -При ручном движении первой пластины: - -- первая движется в выбранном направлении; -- вторая движется в том же направлении; -- третья движется в противоположном направлении. - -Движение второй пластины не затрагивает остальные. Движение третьей затрагивает первую, но не обязано повторять связи первой строки. - -## 3. Правило перехода - -Одна пользовательская команда имеет вид: - -```text -Пластина K - направление xS -``` - -где `K` - номер выбранной пластины, а `S` - количество делений от 1 до 6. - -Команда рассчитывается атомарно: - -1. Выбираются исходная пластина, направление и количество делений. -2. Исходная пластина сдвигается на `S` делений. -3. Все ненулевые связи из строки исходной пластины применяются один раз с тем же множителем `S`. -4. Связанные пластины не запускают собственные строки связей. Каскада и рекурсии нет. -5. Проверяются границы всех затронутых пластин. - -Если хотя бы одна затронутая пластина должна выйти за диапазон `1..7`: - -- весь переход считается заблокированным; -- ни одна пластина не перемещается; -- исходное состояние остается неизменным. - -Заблокированный переход не включается в найденную комбинацию. Решатель генерирует только полностью допустимые команды; частичное выполнение до первой блокировки не считается отдельным переходом. - -## 4. Формат входного файла - -Предварительно согласованный формат: - -```json -{ - "state": [7, 4, 1, 3, 6], - "links": [ - [0, 1, 0, 0, -1], - [0, 0, 0, 0, 0], - [1, 0, 0, 1, 0], - [0, 0, 0, 0, 1], - [0, -1, 0, 0, 0] - ] -} -``` - -Обязательная валидация: - -- `state` - массив длины от 2 до 7; -- каждое состояние - целое число от 1 до 7; -- `links` - квадратная матрица того же размера; -- каждый коэффициент - только `-1`, `0` или `1`; -- каждый диагональный коэффициент - `0`. - -Некорректный вход должен завершать программу с понятной ошибкой и ненулевым кодом выхода. - -## 5. Алгоритм - -Выбран поиск в ширину по конечному графу состояний. - -Это именно граф, а не обычное дерево: одно состояние может быть получено разными последовательностями, а каждый успешный переход обратим противоположным движением той же исходной пластины. Повторно посещать уже обработанное состояние нельзя. - -Поиск в ширину выбран потому, что он: - -- гарантирует кратчайшее решение согласно окончательно выбранной стоимости перехода; -- определяет, что цель недостижима; -- имеет предсказуемые верхние границы; -- не требует эвристики или очереди с приоритетом. - -Количество возможных состояний: - -```text -S = 7^N -``` - -При `N = 7`: - -```text -S = 823 543 -``` - -Для одной пластины из любого состояния существует не более шести возможных ненулевых сдвигов суммарно по обоим направлениям. Поэтому: - -- не более `6N = 42` допустимых команд-кандидатов из состояния; -- не более `34 588 806` сгенерированных переходов по всему пространству при `N = 7`; -- не более примерно `242,2 млн` простых проверок координат при наивной проверке каждой команды; -- ориентировочно `7-8 МБ` служебной памяти при числовом кодировании состояний и использовании типизированных массивов. - -Временная сложность в худшем случае: - -```text -O(N^2 * 7^N) -``` - -Память: - -```text -O(7^N) -``` - -Рекомендуемое внутреннее представление состояния - одно целое число, кодирующее позиции пластин в системе счисления по основанию 7. - -Двунаправленный поиск, A*, Dijkstra и IDDFS не входят в первую версию. Их можно пересмотреть только после измерения обычного поиска в ширину на предельных случаях. - -### 5.1. Компоненты вычислительного ядра - -Решение разделяется на независимые модули: - -1. Загрузка и валидация входного JSON. -2. Кодирование и декодирование состояния числом в системе счисления по основанию 7. -3. Чистая функция применения одной команды к состоянию. -4. Генератор всех допустимых команд из состояния. -5. Поиск в ширину с таблицами посещенных состояний, родителей и команд. -6. Восстановление последовательности команд. -7. Формирование JSON-результата и человекочитаемого консольного вывода. -8. CLI-оболочка для аргументов, кодов выхода и записи файла. - -Вычислительное ядро не должно зависеть от CLI или файловой системы, чтобы позднее его можно было перенести на сайт. - -## 6. Выбор среди нескольких решений - -Стоимость одного перехода равна одной пользовательской команде независимо от количества делений `S`. - -Главный критерий оптимальности: - -```text -минимальное количество команд -``` - -Это напрямую минимизирует количество переключений между пластинами. Две соседние команды для одной пластины всегда можно объединить или сократить, поэтому они не могут входить в минимальное решение как отдельные команды. - -Одна команда сразу отображается в сгруппированном виде: - -```text -Пластина 3 - влево x5 -``` - -Если существует несколько решений с одинаковым минимальным количеством команд, поиск использует стабильный порядок: - -1. меньший номер пластины; -2. движение влево перед движением вправо; -3. большее количество делений перед меньшим. - -FIFO-очередь поиска в ширину и такой порядок генерации переходов дают детерминированно первое решение среди равных. - -## 7. Интерфейс командной строки - -Входной файл обязателен: - -```bash -node solve-lock.mjs lock.json -``` - -По умолчанию комбинация выводится в консоль. - -Для записи результата в файл: - -```bash -node solve-lock.mjs lock.json --output solution.json -``` - -Правила `--output`: - -- отсутствующий файл создается; -- существующий файл полностью перезаписывается; -- без флага никакой выходной файл не создается. -- с флагом JSON-файл записывается дополнительно, а человекочитаемый результат по-прежнему выводится в консоль. - -Коды завершения: - -- `0` - решение найдено или замок уже находится в целевом состоянии; -- `1` - ошибка входных данных, аргументов CLI, чтения или записи; -- `2` - корректный замок не имеет решения из указанного состояния. - -## 8. Структура результата - -JSON-результат должен позволять проверить решение программно и показать его человеку: - -- `metrics.commands` - количество пользовательских команд; -- `metrics.divisions` - сумма `steps` выбранных пластин без дополнительного подсчета движений связанных пластин; -- `metrics.plateSwitches` - количество смен выбранной пластины между соседними командами. - -```json -{ - "status": "solved", - "initialState": [7, 4, 1], - "targetState": [4, 4, 4], - "commands": [ - { - "plate": 1, - "direction": "right", - "steps": 3 - } - ], - "finalState": [4, 4, 4], - "metrics": { - "commands": 1, - "divisions": 3, - "plateSwitches": 0 - } -} -``` - -Если решения нет: - -```json -{ - "status": "unsolvable", - "initialState": [1, 1, 1], - "targetState": [4, 4, 4], - "commands": [] -} -``` - -Консольное представление найденного решения: - -```text -Найдено команд: 2 -1. Пластина 1 - влево x3 -2. Пластина 3 - вправо x2 -``` - -Для уже открытого замка выводится `Замок уже открыт.`. Для недостижимой цели выводится `Решение не найдено.`. - -## 9. Требования к тестированию - -Минимальный набор сценариев: - -1. Начальное состояние уже является целевым. -2. Независимые пластины без связей. -3. Прямая связь `1`. -4. Инвертированная связь `-1`. -5. Несимметричные направленные связи. -6. Отсутствие каскадного применения связей. -7. Полная блокировка атомарного перехода на границе. -8. Обнаружение недостижимой цели. -9. Гарантия минимального количества сгруппированных команд. -10. Команда со сдвигом сразу на несколько делений. -11. Блокировка всей команды, если связанная пластина не может пройти все запрошенные деления. -12. Правило выбора среди нескольких равных решений. -13. Вывод в консоль. -14. Создание нового выходного файла. -15. Перезапись существующего выходного файла. -16. Ошибки валидации входного JSON. - -## 10. Границы первой версии - -В первую версию не входят: - -- сайт или графический интерфейс; -- автоматическое распознавание состояния по изображению; -- автоматическое определение связей пластин; -- искусственный интеллект; -- база известных игровых замков; -- анализ приложенного PDF; -- оптимизация алгоритма без подтвержденной проблемы производительности. - -## 11. Проектный skill - -После реализации и проверки решателя должен быть создан проектный skill, который зафиксирует: - -- как интерактивно собирать исходное состояние замка; -- как проверять направленные связи каждой пластины; -- как переводить наблюдения в `lock.json`; -- как запускать решатель; -- как проверять найденную комбинацию в игре; -- как уточнять модель, если фактический переход отличается от ожидаемого. - -Skill создается после реализации и проверки интерфейсов, чтобы его инструкции соответствовали фактическому поведению утилиты. diff --git a/docs/superpowers/specs/2026-09-07-matrix-search.md b/docs/superpowers/specs/2026-09-07-matrix-search.md deleted file mode 100644 index 5442d8f..0000000 --- a/docs/superpowers/specs/2026-09-07-matrix-search.md +++ /dev/null @@ -1,38 +0,0 @@ -# Матричный поиск и воспроизводимое сравнение - -Дата: 2026-09-07. Основание: пользователь попросил закрепить проверенный матричный алгоритм в собственном проекте, добавить 45 замков в проверки и бенчмарки, формировать доказательный отчёт, после этого готовить перенос в UnlockMyLoot. Отдельный сайт в эту работу не входит. - -Эта спецификация обновляет пункты о размере, алгоритме, критерии равенства и наборе данных спецификации 1.0. Остальные правила механики и формат команд сохраняются. - -## Контракт - -- ES modules, Node.js 20+ для CLI и проверок, без сторонних runtime-зависимостей. -- Вычислительные модули не используют Node.js API, DOM, файловую систему или сеть. Они пригодны для импорта в браузер и Worker. -- По уточнению пользователя добавляется готовый classic-script файл для подключения через обычный ` - -``` - -Модульный вариант использует CDN URL `.mjs` в `script type="module"` через import. -Node.js импортирует установленный пакет или локальный `.mjs`; прямой HTTPS-import -стандартным Node loader не входит в контракт. Все четыре формата до публикации -проверяются на статическом HTTP-сервере, после публикации — по реальным CDN URL. -Ссылки закрепляют точную версию; обновление и откат требуют явной замены версии. -Четыре файла существуют в npm tarball заранее. CDN не выполняет нашу минификацию -или сборку: не используем +esm, combine, сокращённый путь и автогенерацию .min. - -Файл evidence остаётся отдельным Release asset и не входит в runtime-загрузку. -Манифест контрольных сумм связывает npm-версию и source SHA с байтами runtime-файлов. -Release notes содержат ссылку на npm-версию, команду установки, ссылки скачивания -и готовые CDN script/import snippets. - -Дополнительная read-only проверка 2026-09-07: у dayjs@1.11.13 файл dayjs.min.js -из npm tarball и `https://cdn.jsdelivr.net/npm/dayjs@1.11.13/dayjs.min.js` совпал -побайтно (7160 bytes, SHA256 -`9cfdb93f38afcf2d076abecd66d32bfd3383cdf1967654ebc26a26605daf4173`). -CDN ответил 200, `Content-Type: application/javascript; charset=utf-8`, -`Access-Control-Allow-Origin: *` на запрос с Origin: https://example.com. -Эта проверка иллюстрирует механизм сервиса и не заменяет проверки нашей сборки. - -## Документация и приёмка - -README и актуальная документация пишутся на английском. README содержит назначение, алгоритмические гарантии, оба входа и все исходы, -координатные конвенции, четыре способа подключения, Worker, поддержанные среды, -команды проверки, чтение performance verdict, изменение snapshot и выпуск. -AGENTS.md описывает реальные модули, инварианты, команды и границы изменений. -Для работы с публичным проектом доступ к частной wiki не требуется. - -Независимый reviewer сверяет финальное решение с public_safe принципами wiki: -TDD, DDD, BDD, SOLID, KISS, YAGNI и DRY. TDD подтверждается наблюдаемым red/green; -DDD выражается точной моделью и единым владельцем инвариантов, BDD — проверяемым -поведением потребителя. Не вводятся фреймворки, слои или классы ради названий. -TypeScript 7 и JavaScript idioms проверяются по официальной актуальной документации. -README ссылается на компактные документы о контракте, алгоритме, сложности, -разработке, benchmark evidence и публикации; AGENTS.md направляет к тем же -источникам, не создаёт дублирующую локальную wiki. Все изменения входят в один -новый draft PR от сохранённой референсной ветки. - -Во всех этапах npm/GitHub Release ниже комплект включает четыре файла ядра и -пятый CLI. CDN/browser-проверка относится к четырём файлам ядра; CLI проверяется -в Node.js из того же tarball. Evidence остаётся отдельным asset, не runtime-файлом. - -Готовность подтверждается проходящим CI нового PR, проверкой всех 45 входов, -принятыми performance-результатами, тестами всех публикуемых форматов и понятным -планом первого выпуска. До согласованного merge стабильная публикация npm/Release -не выполняется; настройка канареек также не считается уже выполненной публикацией. - -## Основания - -- Прочитаны инженерные принципы владельца: SOLID/KISS/YAGNI/DRY, реальные пути - импорта и проверка распространяемого артефакта; разделение deterministic и - environmental verdict. Использованы общие правила, помеченные public_safe. -- [Vite library build options](https://vite.dev/config/build-options) -- [Vite и проверка TypeScript](https://vite.dev/guide/features#typescript) -- [Vitest Browser Mode](https://vitest.dev/guide/browser/) -- [Vitest benchmarking](https://vitest.dev/guide/benchmarking) -- [typescript-eslint: поддержанные версии](https://typescript-eslint.io/users/dependency-versions/) -- [Oxlint](https://oxc.rs/docs/guide/usage/linter.html) -- [Agent Browser](https://github.com/vercel-labs/agent-browser) -- [Vitest Playwright provider](https://vitest.dev/config/browser/playwright) -- [TypeScript strict](https://www.typescriptlang.org/tsconfig/strict.html) -- [Публичные npm-пакеты](https://docs.npmjs.com/about-public-packages/) -- [npm package.json: правила имени](https://docs.npmjs.com/cli/v11/configuring-npm/package-json/#name) -- [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) -- [npm publish: tarball и неизменяемость версии](https://docs.npmjs.com/cli/v11/commands/npm-publish/) -- [GitHub Actions: закрепление SHA и обновления](https://docs.github.com/en/actions/reference/security/secure-use) -- [Официальный GitHub CLI: создание Release](https://cli.github.com/manual/gh_release_create) -- [GitHub Release assets](https://docs.github.com/en/rest/releases/assets) -- [GitHub immutable releases](https://docs.github.com/en/code-security/concepts/supply-chain-security/immutable-releases) -- [Официальный npm registry: metadata и tarball](https://github.com/npm/registry/blob/main/docs/responses/package-metadata.md) -- [npm dist-tags для stable/canary](https://docs.npmjs.com/adding-dist-tags-to-packages/) -- [GitHub Packages npm: обязательная авторизация](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#authenticating-to-github-packages) -- [jsDelivr: автоматическая раздача npm](https://github.com/jsdelivr/jsdelivr#npm) -- [UNPKG: документированный URL отдельного файла](https://unpkg.com/) -- [Бесплатность публичных npm-пакетов](https://docs.npmjs.com/about-npm/#sharing-packages-and-collaborating-with-others) -- [GitHub Actions: бесплатные standard runners и квоты](https://docs.github.com/en/billing/concepts/product-billing/github-actions) -- Версии выше получены из npm registry `//latest` 2026-09-07; - peerDependencies и engines прочитаны до предложения стека. diff --git a/docs/upstream-contribution.md b/docs/upstream-contribution.md deleted file mode 100644 index 3495de4..0000000 --- a/docs/upstream-contribution.md +++ /dev/null @@ -1,25 +0,0 @@ -# Дополнительный режим для UnlockMyLoot - -Это внутренний план предложения, не отправленное обращение или upstream PR. - -## Пользовательская задача - -При игре с геймпадом игрок может предпочесть меньше переключений между пластинами, даже если приходится сделать больше единичных сдвигов. Нынешний UnlockMyLoot сначала минимизирует единичные сдвиги. Поэтому дополнительный режим решает другой пользовательский приоритет и не объявляет текущий решатель ошибочным. - -Проверка на закреплённых 45 конфигурациях и отчёт находятся в `docs/benchmarks`. Авторство исходного каталога и происхождение каждого входа указаны в fixtures. Это сравнение конкретных конфигураций и версий, не утверждение превосходства во всех сценариях. - -## Как предложить - -1. Сначала сохранить реализацию, независимые проверки и измерения в gothic-lock-solver. -2. По уточнению владельца проекта подготовить сразу два альтернативных draft PR: минимальное дополнение внутри принятой структуры кода и вариант с отдельным готовым файлом вычислительного ядра. -3. В обоих вариантах сохранить текущий решатель и добавить необязательный режим минимума действий. Приложить воспроизводимое сравнение всех 45 конфигураций, явно показывающее компромисс A/C и условия измерения времени. -4. Точные описания согласовать с владельцем нашего проекта перед открытием PR. Два варианта являются альтернативами для обсуждения, а не последовательными обязательными изменениями. -5. Формат окончательного включения выбирает maintainer. npm, CDN, чужой API и новый процесс сборки не являются обязательными условиями: готовый JS-файл можно хранить в самом репозитории. - -## Почему важен формат - -В [статье автора](https://dtf.ru/games/5121406-kak-vskryt-zamki-v-gothic-1-remake) описаны самостоятельное изучение задачи, попытка линейного решения и переход к поиску допустимого пути из-за границ. Матричная эвристика сохраняет необходимость этого поиска и ускоряет его; она не опровергает этот вывод. - -[Репозиторий UnlockMyLoot](https://github.com/1h8s/unlockmyloot) подчёркивает отсутствие сборки и зависимостей, а также авторство мастера определения связей. В предложение следует включать стоимость поддержки и совместимость с этим устройством проекта. В частности, обычный внешний ES-модуль может менять поведение запуска через `file://`; этот сценарий нужно отдельно согласовать, не навязывая перенос на npm. - -Сайт, визуализация, сбор и публикация конфигураций, мастер определения связей и существующий решатель остаются вкладом их авторов. Авторство нашего математического дополнения фиксируется в коммитах и пояснении PR. Личную реакцию автора заранее установить нельзя; решение о включении и предпочтительном формате принимает maintainer. diff --git a/package.json b/package.json index 8e9238e..5ce3971 100644 --- a/package.json +++ b/package.json @@ -7,21 +7,20 @@ "node": "^22.0.0 || ^24.0.0 || ^26.0.0" }, "scripts": { - "lint": "oxlint --deny-warnings src cli scripts tests benchmarks *.ts", + "lint": "oxlint --deny-warnings src tests .github/scripts *.ts", "typecheck": "tsc --noEmit", "check": "pnpm lint && pnpm typecheck", "test": "pnpm check && pnpm test:coverage && pnpm build && pnpm test:distribution && pnpm test:browser && pnpm test:e2e && pnpm verify:package", "test:node": "vitest run --project node", "test:distribution": "vitest run --project distribution", "test:browser": "vitest run --project chromium --project firefox --project webkit", - "test:e2e": "playwright test", + "test:e2e": "node tests/e2e/build-fixtures.ts && playwright test", "test:coverage": "vitest run --project node --coverage", - "build": "node scripts/build.ts", - "verify:package": "node scripts/verify-package.ts", - "benchmark": "node scripts/benchmark.ts", - "benchmark:compare": "node scripts/benchmark-compare.ts", - "benchmark:vitest": "vitest bench --run --project \"node (bench)\"", - "solve": "node cli/main.ts" + "build": "node .github/scripts/build.ts", + "verify:package": "node tests/e2e/verify-package.ts", + "benchmark": "node tests/benchmarks/benchmark.ts", + "benchmark:compare": "node tests/benchmarks/compare.ts", + "solve": "node src/cli.ts" }, "description": "Exact shortest-action solver for Gothic 1 Remake plate locks.", "license": "Apache-2.0", diff --git a/patches/vitest@5.0.0.patch b/patches/vitest@5.0.0.patch deleted file mode 100644 index 93fd90b..0000000 --- a/patches/vitest@5.0.0.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/config.d.ts b/config.d.ts -index 36afa874abb35e02a7bb36d1ae0858e2a8b66b96..cd1f8bd910b018645c4bdc67f4a61a9f27268ec6 100644 ---- a/config.d.ts -+++ b/config.d.ts -@@ -1,3 +1,3 @@ --// ensure `@vitest/expect` provides `chai` types --import type {} from '@vitest/expect' -+// Vitest 5 bundles its expect declarations; chai remains a direct dependency. -+import type {} from 'chai' - export * from './dist/config.js' -diff --git a/dist/chunks/plugin.d.BbcoZhuj.d.ts b/dist/chunks/plugin.d.BbcoZhuj.d.ts -index f399a7658b43c34df38c976e6f41744d9af7a5fd..811802c8a90a5910edd9d708f70ef78bc8c5cd09 100644 ---- a/dist/chunks/plugin.d.BbcoZhuj.d.ts -+++ b/dist/chunks/plugin.d.BbcoZhuj.d.ts -@@ -3318,7 +3318,7 @@ interface BenchmarkUserOptions { - * - * @experimental - */ -- provider?: string; -+ provider?: string | undefined; - /** - * Disable warnings when a benchmark accesses module export getters too many times. - * @default false -@@ -3611,7 +3611,7 @@ interface InlineConfig { - * Specifies the memory limit for `worker_thread` or `child_process` before they are recycled. - * If you see memory leaks, try to tinker this value. - */ -- vmMemoryLimit?: string | number; -+ vmMemoryLimit?: string | number | undefined; - /** - * Pool used to run tests in. - * diff --git a/playwright.config.ts b/playwright.config.ts index 64c2b44..9e71f88 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -8,7 +8,8 @@ export default defineConfig({ retries: 0, workers: 2, timeout: 60_000, - reporter: [['list'], ['html', { open: 'never' }]], + reporter: [['list'], ['html', { open: 'never', outputFolder: 'artifacts/playwright-report' }]], + outputDir: 'artifacts/test-results', use: { baseURL: 'http://127.0.0.1:4177', serviceWorkers: 'block', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e68b9c9..329c428 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -105,9 +105,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - vitest@5.0.0: b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c - importers: .: @@ -138,7 +135,7 @@ importers: version: 8.2.2(@types/node@26.4.1) vitest: specifier: 5.0.0 - version: 5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) + version: 5.0.0(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) packages: @@ -1148,7 +1145,7 @@ snapshots: '@vitest/mocker': 5.0.0(vite@8.2.2(@types/node@26.4.1)) playwright: 1.63.0 tinyrainbow: 3.1.1 - vitest: 5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) + vitest: 5.0.0(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) transitivePeerDependencies: - bufferutil - msw @@ -1165,7 +1162,7 @@ snapshots: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.1 - vitest: 5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) + vitest: 5.0.0(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) ws: 8.21.3 transitivePeerDependencies: - bufferutil @@ -1183,7 +1180,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) + vitest: 5.0.0(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) optionalDependencies: '@vitest/browser': 5.0.0(vite@8.2.2(@types/node@26.4.1))(vitest@5.0.0) @@ -1216,7 +1213,7 @@ snapshots: pathe: 2.0.3 sirv: 3.0.2 tinyrainbow: 3.1.1 - vitest: 5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) + vitest: 5.0.0(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)) '@vitest/utils@5.0.0': dependencies: @@ -1450,7 +1447,7 @@ snapshots: '@types/node': 26.4.1 fsevents: 2.3.3 - vitest@5.0.0(patch_hash=b167f53e125b488550d8e92e81a0def6c08d492fdc13ee8a2f38e625ef39d29c)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)): + vitest@5.0.0(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-v8@5.0.0)(@vitest/ui@5.0.0)(vite@8.2.2(@types/node@26.4.1)): dependencies: '@types/chai': 5.2.3 '@vitest/mocker': 5.0.0(vite@8.2.2(@types/node@26.4.1)) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml deleted file mode 100644 index 0704ca8..0000000 --- a/pnpm-workspace.yaml +++ /dev/null @@ -1,2 +0,0 @@ -patchedDependencies: - vitest@5.0.0: patches/vitest@5.0.0.patch diff --git a/scripts/benchmark-compare.ts b/scripts/benchmark-compare.ts deleted file mode 100644 index aa3608b..0000000 --- a/scripts/benchmark-compare.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { main } from '../benchmarks/cli.ts'; -try { await main(true); } catch (error) { console.error(error); process.exitCode = 1; } diff --git a/scripts/benchmark-memory.ts b/scripts/benchmark-memory.ts deleted file mode 100644 index 70b57de..0000000 --- a/scripts/benchmark-memory.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { randomUUID } from 'node:crypto'; -import { readFileSync } from 'node:fs'; -import { mkdir, mkdtemp, rename, rm, writeFile } from 'node:fs/promises'; -import { basename, dirname, join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { collectMetadata, parseBenchmarkArgs } from '../benchmarks/cli.ts'; -import { jsonFile, loadCatalog, sha256 } from '../benchmarks/catalog.ts'; -import { collectMemory, type MemorySpec } from '../benchmarks/memory.ts'; -import { record } from '../benchmarks/validation.ts'; - -const [priorPath, ...args] = process.argv.slice(2); -if (priorPath === undefined || priorPath.startsWith('--')) throw new Error('Usage: node scripts/benchmark-memory.ts PRIOR_BENCHMARK_JSON [--reference PATH] [--output-dir PATH]'); -const options = parseBenchmarkArgs(args); -const metadata = collectMetadata(options); -const previous = record(jsonFile(priorPath)); -if (record(record(previous['metadata'])['candidate'])['moduleSha256'] !== metadata.candidate.moduleSha256) throw new Error('Candidate bytes differ from the earlier timing report; run a full new comparison.'); -const specs: MemorySpec[] = [ { name: 'candidate', module: options.candidateModule, format: 'tuple' }, - { name: 'baseline', module: options.baselineModule, format: options.baselineFormat }, - { name: 'originalBfs', module: fileURLToPath(new URL('../benchmarks/baseline/src/index.mjs', import.meta.url)), format: 'legacy' } ]; -if (options.reference !== undefined) specs.push({ name: 'upstream', module: options.reference, format: 'upstream' }); -const catalog = loadCatalog(); const locks = options.smoke ? catalog.slice(0, 2) : catalog; -const memory = collectMemory(locks, specs, options.memoryRepetitions, (line) => process.stderr.write(`${line}\n`)); -const createdAt = new Date().toISOString(); -const correction = { schemaVersion: 1, createdAt, scope: 'memory-only-correction', - supersedes: { report: resolve(priorPath), sha256: sha256(readFileSync(priorPath)), component: 'memory', - reason: 'Earlier raw getrusage peaks retained a pre-exec launch floor. Its memory pass is invalid; timing and deterministic quality remain unchanged.' }, metadata, memory }; -const outputRoot = resolve(options.outputDir); await mkdir(outputRoot, { recursive: true }); -const staged = await mkdtemp(join(outputRoot, '.memory-')); -const directory = join(outputRoot, `memory-${createdAt.replaceAll(/[^0-9TZ]/g, '')}-${randomUUID().slice(0, 8)}`); -const lines = ['# Corrected isolated RSS evidence', '', `Memory verdict: **${memory.performance.verdict}**. This supplement supersedes the earlier report’s memory measurements and memory pass. Its timing and deterministic quality observations remain valid.`, '', - memory.metric, '', memory.accuracy, '', '| Implementation | Median peak RSS (MiB) | p95 (MiB) |', '| --- | ---: | ---: |']; -for (const [name, measurement] of Object.entries(memory.wholeCatalog)) lines.push(`| ${name} | ${(measurement.peakRssBytes.median / 1048576).toFixed(2)} | ${(measurement.peakRssBytes.p95 / 1048576).toFixed(2)} |`); -lines.push('', `Method: [Linux kernel /proc documentation](${memory.sources[0]}) and [Node resourceUsage documentation](${memory.sources[1]}). RSS remains approximate process memory, including runtime and measurement infrastructure; browser peak memory is unavailable.`, '', 'The JSON contains every individual peak, startup/current RSS, raw getrusage diagnostic, control sample, bounded repeat, source and artifact hash.', ''); -try { - await writeFile(join(staged, 'memory.json'), `${JSON.stringify(correction, null, 2)}\n`, { flag: 'wx' }); - await writeFile(join(staged, 'memory.md'), lines.join('\n'), { flag: 'wx' }); - await rename(staged, directory); -} catch (error) { await rm(staged, { recursive: true, force: true }); throw error; } -const notice = `# Superseded memory measurements\n\nThe memory measurements and memory pass in this run are invalid: raw Node getrusage maxRSS retained a pre-exec launch high-water. The original hashed files remain unchanged for audit. Use [the corrected RSS supplement](../${basename(directory)}/memory.md) and its full JSON. Timing and deterministic quality observations are unaffected.\n`; -await writeFile(join(dirname(resolve(priorPath)), 'MEMORY-SUPERSEDED.md'), notice, { flag: 'wx' }); -console.log(JSON.stringify({ directory, verdict: memory.performance.verdict, supersedes: correction.supersedes }, null, 2)); diff --git a/scripts/benchmark-snapshot.ts b/scripts/benchmark-snapshot.ts deleted file mode 100644 index 8c26e4f..0000000 --- a/scripts/benchmark-snapshot.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { existsSync, readFileSync } from 'node:fs'; -import { dirname, resolve } from 'node:path'; -import { catalogSha256, git, loadCatalog, sha256, verifySource } from '../benchmarks/catalog.ts'; -import { string } from '../benchmarks/validation.ts'; -import { loadImplementation, runComparison, writeSnapshot } from '../benchmarks/harness.ts'; -const fields = new Map(); -const args = process.argv.slice(2); -for (let index = 0; index < args.length; index += 2) { - const key = args[index]; const value = args[index + 1]; - if (key === undefined || value === undefined || !['--source-module', '--source-format', '--reason', '--output'].includes(key) || fields.has(key)) throw new Error('Invalid snapshot arguments.'); - fields.set(key, value); -} -const modulePath = fields.get('--source-module'); const format = fields.get('--source-format') ?? 'tuple'; -const reason = fields.get('--reason'); const output = fields.get('--output'); -if (modulePath === undefined || reason === undefined || reason.trim() === '' || output === undefined || (format !== 'tuple' && format !== 'legacy')) throw new Error('Usage: node scripts/benchmark-snapshot.ts --source-module PATH --source-format tuple|legacy --reason TEXT --output NEW_PATH'); -const solver = await loadImplementation(modulePath, format, 'Snapshot source'); -const report = runComparison({ locks: loadCatalog(), candidate: solver, baseline: solver, repetitions: 2, warmups: 1 }); -const sourceDirectory = dirname(dirname(resolve(modulePath))); -const sourceRevision = existsSync(resolve(sourceDirectory, 'manifest.json')) - ? string(verifySource(sourceDirectory)['revision']) - : git(dirname(resolve(modulePath)), ['rev-parse', 'HEAD']); -const snapshot = { schemaVersion: 1, fixtureSha256: catalogSha256, - source: { revision: sourceRevision, moduleSha256: sha256(readFileSync(modulePath)), format }, reason, - locks: report.results.map((row) => { - const value = row.solvers['candidate']; if (value === undefined) throw new Error('Missing snapshot solution.'); - return { id: row.id, optimumA: row.optimumA, A: value.A, U: value.U, C: value.C, plateSwitches: value.plateSwitches, commands: value.commands }; - }) }; -await writeSnapshot(output, snapshot); -console.log(JSON.stringify({ snapshotPath: resolve(output), lockCount: snapshot.locks.length, source: snapshot.source })); diff --git a/scripts/benchmark.ts b/scripts/benchmark.ts deleted file mode 100644 index a703120..0000000 --- a/scripts/benchmark.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { main } from '../benchmarks/cli.ts'; -try { await main(false); } catch (error) { console.error(error); process.exitCode = 1; } diff --git a/solve-lock.mjs b/solve-lock.mjs deleted file mode 100755 index 277f57c..0000000 --- a/solve-lock.mjs +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env node -import { runCli } from './cli/cli.ts'; - -process.exitCode = await runCli(process.argv.slice(2)); diff --git a/src/matrix-search.ts b/src/astar.ts similarity index 82% rename from src/matrix-search.ts rename to src/astar.ts index beed0ce..a366524 100644 --- a/src/matrix-search.ts +++ b/src/astar.ts @@ -1,13 +1,38 @@ import { SearchLimitError } from './errors.ts'; -import { readAt, readByte } from './indexed.ts'; -import { IndexedHeap } from './indexed-heap.ts'; -import type { SearchNode } from './indexed-heap.ts'; -import type { LockModel } from './lock-model.ts'; -import { analyzeMatrix } from './matrix-analysis.ts'; -import { BfsSearch } from './search-bfs.ts'; -import { commandFor, decodeDigits, movementLimits } from './search-limits.ts'; -import type { PreparedSearch, SearchBudget } from './search-limits.ts'; -import type { Command, SearchStrategy } from './types.ts'; +import { commandFor, decodeDigits, movementLimits, readAt, readByte } from './lock.ts'; +import { IndexedHeap } from './priority-queue.ts'; +import type { SearchNode } from './priority-queue.ts'; +import type { LockModel, PreparedSearch } from './lock.ts'; +import { analyzeMatrix } from './matrix.ts'; +import { BfsSearch } from './bfs.ts'; +import { createSolverConfig } from './config.ts'; +import type { SolverConfig } from './config.ts'; +import type { Command } from './types.ts'; + +/** One counter owner shared by the certificate, A* and singular BFS. */ +export class SearchBudget { + readonly options: SolverConfig; + private visited = 0; + private expanded = 0; + + constructor(options: SolverConfig = createSolverConfig()) { + this.options = options; + } + + check(name: keyof SolverConfig, used: number): void { + if (used > this.options[name]) throw new SearchLimitError(name, this.options[name], used); + } + + visit(): void { + this.check('maxVisited', this.visited + 1); + this.visited += 1; + } + + expand(): void { + this.check('maxExpanded', this.expanded + 1); + this.expanded += 1; + } +} /** * Sufficient certificate only: legal single actions for nonzero net entries @@ -57,7 +82,7 @@ function reconstruct(node: SearchNode): readonly Command[] { * changes one r[i] by at most six, so h = sum ceil(abs(r[i])/6) is consistent. * Closed nodes are final and the first popped goal minimizes action count. */ -export class MatrixSearch implements SearchStrategy { +export class MatrixSearch { private readonly model: LockModel; private readonly prepared: PreparedSearch; private readonly budget: SearchBudget; diff --git a/src/search-bfs.ts b/src/bfs.ts similarity index 92% rename from src/search-bfs.ts rename to src/bfs.ts index c683870..ca936ed 100644 --- a/src/search-bfs.ts +++ b/src/bfs.ts @@ -1,8 +1,8 @@ import { SearchLimitError } from './errors.ts'; -import { readAt, readByte, readInt32 } from './indexed.ts'; -import { commandFor, decodeDigits, movementLimits } from './search-limits.ts'; -import type { PreparedSearch, SearchBudget } from './search-limits.ts'; -import type { Command, SearchStrategy } from './types.ts'; +import { commandFor, decodeDigits, movementLimits, readAt, readByte, readInt32 } from './lock.ts'; +import type { PreparedSearch } from './lock.ts'; +import type { SearchBudget } from './astar.ts'; +import type { Command } from './types.ts'; interface SparseRecord { readonly parent: number; @@ -36,7 +36,7 @@ function reconstruct(records: Records, source: number, code: number): readonly C } /** Exact action BFS; source index, positive sign, then larger steps win ties. */ -export class BfsSearch implements SearchStrategy { +export class BfsSearch { private readonly prepared: PreparedSearch; private readonly budget: SearchBudget; diff --git a/cli/cli.ts b/src/cli.ts similarity index 58% rename from cli/cli.ts rename to src/cli.ts index 873a8d2..df45b9f 100644 --- a/cli/cli.ts +++ b/src/cli.ts @@ -1,7 +1,11 @@ +#!/usr/bin/env node +import { realpathSync } from 'node:fs'; import { readFile, writeFile } from 'node:fs/promises'; -import { solveLock } from '../src/index.ts'; -import { LockModel } from '../src/lock-model.ts'; -import type { Command, Position } from '../src/types.ts'; +import { fileURLToPath } from 'node:url'; +import { validateSolverConfig } from './config.ts'; +import { createSolverConfig, solveLock } from './index.ts'; +import { LockModel } from './lock.ts'; +import type { Command, Position } from './types.ts'; interface TextWriter { write(text: string): unknown; @@ -12,7 +16,7 @@ interface Streams { readonly stderr: TextWriter; } -interface LegacyCommand { +interface CliCommand { readonly plate: number; readonly direction: 'left' | 'right'; readonly steps: number; @@ -21,7 +25,7 @@ interface LegacyCommand { interface ResultBase { readonly initialState: readonly Position[]; readonly targetState: readonly Position[]; - readonly commands: readonly LegacyCommand[]; + readonly commands: readonly CliCommand[]; } type CliResult = ResultBase & ( @@ -33,31 +37,45 @@ type CliResult = ResultBase & ( } ); -function parseArgs(argv: readonly string[]): { inputPath: string; outputPath: string | null } { - const [input, flag, output] = argv; - if (typeof input === 'string' && input.length > 0 && !input.startsWith('--')) { - if (argv.length === 1) return { inputPath: input, outputPath: null }; - if (argv.length === 3 && flag === '--output' && typeof output === 'string' && output.length > 0 && !output.startsWith('--')) { - return { inputPath: input, outputPath: output }; +const usage = 'Использование: gothic-lock-solver [--config ] [--output ]\n gothic-lock-solver --help'; + +function parseArgs(argv: readonly string[]): { inputPath: string; configPath: string | null; outputPath: string | null } { + const inputPath = argv[0]; + if (typeof inputPath !== 'string' || inputPath.length === 0 || inputPath.startsWith('--')) throw new Error(usage); + let configPath: string | null = null; + let outputPath: string | null = null; + for (let index = 1; index < argv.length; index += 2) { + const flag = argv[index]; + const path = argv[index + 1]; + if (typeof path !== 'string' || path.length === 0 || path.startsWith('--')) throw new Error(usage); + if (flag === '--config' && configPath === null) { + configPath = path; + } else if (flag === '--output' && outputPath === null) { + outputPath = path; + } else { + throw new Error(usage); } } - throw new Error('Использование: gothic-lock-solver [--output ]'); + return { inputPath, configPath, outputPath }; } -function parseInput(json: string): LockModel { - let value: unknown; +function parseJson(json: string): unknown { try { - value = JSON.parse(json); + return JSON.parse(json); } catch { throw new Error('Некорректный JSON.'); } +} + +function parseInput(json: string): LockModel { + const value = parseJson(json); if (typeof value !== 'object' || value === null || Array.isArray(value) || !('state' in value) || !('links' in value)) { throw new Error('Ожидается объект с полями state и links.'); } return new LockModel(value.state, value.links); } -/** Preserve the file/console adapter's historical one-based, physical-direction notation. */ +/** CLI files use one-based plates and physical-direction notation. */ function createResult(state: readonly Position[], solution: readonly Command[] | null): CliResult { const initialState = [...state]; const targetState = state.map((): Position => 4); @@ -65,7 +83,7 @@ function createResult(state: readonly Position[], solution: readonly Command[] | let previous: number | undefined; let divisions = 0; let plateSwitches = 0; - const commands: LegacyCommand[] = []; + const commands: CliCommand[] = []; for (const [index, delta] of solution) { if (previous !== undefined && previous !== index) plateSwitches += 1; previous = index; @@ -89,9 +107,16 @@ function formatResult(result: CliResult): string { /** Exit status: 0 solved, 2 proved unreachable, 1 malformed input/IO/resource failure. */ export async function runCli(argv: readonly string[], streams: Streams = process): Promise<0 | 1 | 2> { try { - const { inputPath, outputPath } = parseArgs(argv); + if (argv.length === 1 && argv[0] === '--help') { + streams.stdout.write(`${usage}\n`); + return 0; + } + const { inputPath, configPath, outputPath } = parseArgs(argv); const model = parseInput(await readFile(inputPath, 'utf8')); - const result = createResult(model.state, solveLock(model.state, model.links)); + const config = configPath === null + ? createSolverConfig() + : createSolverConfig(validateSolverConfig(parseJson(await readFile(configPath, 'utf8')), true)); + const result = createResult(model.state, solveLock(model.state, model.links, config)); if (outputPath !== null) await writeFile(outputPath, `${JSON.stringify(result, null, 2)}\n`, 'utf8'); streams.stdout.write(`${formatResult(result)}\n`); return result.status === 'unsolvable' ? 2 : 0; @@ -100,3 +125,15 @@ export async function runCli(argv: readonly string[], streams: Streams = process return 1; } } + +function isEntryPoint(): boolean { + const entryPath = process.argv[1]; + if (entryPath === undefined) return false; + try { + return realpathSync(entryPath) === realpathSync(fileURLToPath(import.meta.url)); + } catch { + return false; + } +} + +if (isEntryPoint()) process.exitCode = await runCli(process.argv.slice(2)); diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..f8676ef --- /dev/null +++ b/src/config.ts @@ -0,0 +1,48 @@ +import { LockInputError } from './errors.ts'; + +/** Per-solve resource limits. Byte and expansion limits may be zero. */ +export interface SolverConfig { + readonly maxVisited: number; + readonly maxExpanded: number; + readonly maxFrontier: number; + readonly maxDenseBytes: number; +} + +const DEFAULTS: SolverConfig = Object.freeze({ + maxVisited: 2_000_000, + maxExpanded: 1_000_000, + maxFrontier: 1_000_000, + maxDenseBytes: 64 * 1024 * 1024, +}); + +function isConfigField(key: PropertyKey): key is keyof SolverConfig { + return key === 'maxVisited' || key === 'maxExpanded' || key === 'maxFrontier' || key === 'maxDenseBytes'; +} + +/** Shared runtime boundary for typed callers, ordinary JavaScript and CLI JSON. */ +export function validateSolverConfig(value: unknown, partial = false): SolverConfig { + if (typeof value !== 'object' || value === null || Array.isArray(value)) { + throw new LockInputError('Конфигурация поиска должна быть объектом.'); + } + const config = { ...DEFAULTS }; + for (const key of Reflect.ownKeys(value)) { + if (!isConfigField(key)) throw new LockInputError(`Неизвестное поле конфигурации: ${String(key)}.`); + const setting: unknown = Reflect.get(value, key); + const minimum = key === 'maxVisited' || key === 'maxFrontier' ? 1 : 0; + if (typeof setting !== 'number' || !Number.isSafeInteger(setting) || setting < minimum) { + throw new LockInputError(`${key} должен быть безопасным целым числом не меньше ${minimum}.`); + } + config[key] = setting; + } + if (!partial) { + for (const key of Object.keys(DEFAULTS)) { + if (!Object.hasOwn(value, key)) throw new LockInputError(`В конфигурации отсутствует поле ${key}.`); + } + } + return Object.freeze(config); +} + +/** Return a complete immutable configuration without changing the overrides. */ +export function createSolverConfig(overrides: Partial = {}): SolverConfig { + return validateSolverConfig(overrides, true); +} diff --git a/src/errors.ts b/src/errors.ts index 8b7f98b..e06083e 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,4 +1,4 @@ -/** Invalid shape, sparse arrays, positions, coefficients or diagonal values. */ +/** Invalid lock shape, positions, links or solver configuration. */ export class LockInputError extends Error { constructor(message: string, options?: ErrorOptions) { super(message, options); diff --git a/src/index.ts b/src/index.ts index be14c3f..c5ff16c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ -import { LockModel } from './lock-model.ts'; -import { MatrixSearch } from './matrix-search.ts'; -import { PreparedSearch, SearchBudget } from './search-limits.ts'; +import { LockModel, PreparedSearch } from './lock.ts'; +import { MatrixSearch, SearchBudget } from './astar.ts'; +import { createSolverConfig, validateSolverConfig } from './config.ts'; +import type { SolverConfig } from './config.ts'; import type { Command, Links, Position, State } from './types.ts'; /** @@ -11,18 +12,22 @@ import type { Command, Links, Position, State } from './types.ts'; * * Fixed tuples must have N >= 2 and matching N×N links. Dynamic arrays undergo * the same shape and value checks at runtime. - * @throws {LockInputError} When either input violates the lock model. + * @throws {LockInputError} When an input or configuration is invalid. * @throws {SearchLimitError} When a computation limit is exceeded (not unsolvable). */ export function solveLock( state: S & (number extends S['length'] ? unknown : S extends State ? unknown : never), links: NoInfer>, + config?: SolverConfig, ): readonly Command[] | null { + const options = config === undefined ? createSolverConfig() : validateSolverConfig(config); const model = new LockModel(state, links); const prepared = new PreparedSearch(model); - return new MatrixSearch(model, prepared, new SearchBudget()).solve(); + return new MatrixSearch(model, prepared, new SearchBudget(options)).solve(); } +export { createSolverConfig } from './config.ts'; +export type { SolverConfig } from './config.ts'; export { LockInputError, SearchLimitError } from './errors.ts'; export type { SearchLimit } from './errors.ts'; export type { Command, Delta, Link, Links, Position, State } from './types.ts'; diff --git a/src/indexed.ts b/src/indexed.ts deleted file mode 100644 index c0a9f55..0000000 --- a/src/indexed.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Checked indexed access for internally dense arrays and bounded state tables. - * Call sites establish bounds through validated dimensions, allocated lengths or - * search membership. The check keeps that invariant executable and avoids type - * assertions / unchecked non-null accesses under noUncheckedIndexedAccess. - */ -export function readAt(values: readonly T[], index: number): T { - const value = values[index]; - if (value === undefined) throw new Error(`Internal indexed-access invariant failed at ${index}.`); - return value; -} - -/** Typed readers keep hot typed-array access separate from object-array shapes. */ -export function readByte(values: Uint8Array, index: number): number { - const value = values[index]; - if (value === undefined) throw new Error(`Internal byte-access invariant failed at ${index}.`); - return value; -} - -export function readInt32(values: Int32Array, index: number): number { - const value = values[index]; - if (value === undefined) throw new Error(`Internal integer-access invariant failed at ${index}.`); - return value; -} diff --git a/src/lock-model.ts b/src/lock-model.ts deleted file mode 100644 index 6c3f743..0000000 --- a/src/lock-model.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { LockInputError } from './errors.ts'; -import type { Link, Position } from './types.ts'; - -function isArray(value: unknown): value is readonly unknown[] { - return Array.isArray(value); -} - -function isPosition(value: unknown): value is Position { - return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 7; -} - -function isLink(value: unknown): value is Link { - return value === -1 || value === 0 || value === 1; -} - -/** Owns a validated immutable copy of the lock, independent of caller mutations. */ -export class LockModel { - readonly state: readonly Position[]; - readonly links: readonly (readonly Link[])[]; - - constructor(state: unknown, links: unknown) { - if (!isArray(state) || state.length < 2) { - throw new LockInputError('state должен содержать не менее 2 пластин.'); - } - const copiedState: Position[] = []; - for (let index = 0; index < state.length; index += 1) { - const position = state[index]; - if (!Object.hasOwn(state, index) || !isPosition(position)) { - throw new LockInputError(`state[${index}] должен быть целым числом от 1 до 7.`); - } - copiedState.push(position); - } - if (!isArray(links) || links.length !== state.length) { - throw new LockInputError(`links должен быть матрицей ${state.length}x${state.length}.`); - } - const copiedLinks: (readonly Link[])[] = []; - for (let source = 0; source < links.length; source += 1) { - const row = links[source]; - if (!Object.hasOwn(links, source) || !isArray(row) || row.length !== state.length) { - throw new LockInputError(`links[${source}] должен содержать ${state.length} элементов.`); - } - const copiedRow: Link[] = []; - for (let target = 0; target < row.length; target += 1) { - const coefficient = row[target]; - if (!Object.hasOwn(row, target) || !isLink(coefficient)) { - throw new LockInputError(`links[${source}][${target}] должен быть -1, 0 или 1.`); - } - if (source === target && coefficient !== 0) { - throw new LockInputError(`links[${source}][${target}] на диагонали должен быть 0.`); - } - copiedRow.push(coefficient); - } - copiedLinks.push(Object.freeze(copiedRow)); - } - this.state = Object.freeze(copiedState); - this.links = Object.freeze(copiedLinks); - } -} diff --git a/src/lock.ts b/src/lock.ts new file mode 100644 index 0000000..47812a2 --- /dev/null +++ b/src/lock.ts @@ -0,0 +1,163 @@ +import { LockInputError, SearchLimitError } from './errors.ts'; +import type { Command, Delta, Link, Position } from './types.ts'; + +function isArray(value: unknown): value is readonly unknown[] { + return Array.isArray(value); +} + +function isPosition(value: unknown): value is Position { + return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 7; +} + +function isLink(value: unknown): value is Link { + return value === -1 || value === 0 || value === 1; +} + +/** Owns a validated immutable copy of the lock, independent of caller mutations. */ +export class LockModel { + readonly state: readonly Position[]; + readonly links: readonly (readonly Link[])[]; + + constructor(state: unknown, links: unknown) { + if (!isArray(state) || state.length < 2) { + throw new LockInputError('state должен содержать не менее 2 пластин.'); + } + const copiedState: Position[] = []; + for (let index = 0; index < state.length; index += 1) { + const position = state[index]; + if (!Object.hasOwn(state, index) || !isPosition(position)) { + throw new LockInputError(`state[${index}] должен быть целым числом от 1 до 7.`); + } + copiedState.push(position); + } + if (!isArray(links) || links.length !== state.length) { + throw new LockInputError(`links должен быть матрицей ${state.length}x${state.length}.`); + } + const copiedLinks: (readonly Link[])[] = []; + for (let source = 0; source < links.length; source += 1) { + const row = links[source]; + if (!Object.hasOwn(links, source) || !isArray(row) || row.length !== state.length) { + throw new LockInputError(`links[${source}] должен содержать ${state.length} элементов.`); + } + const copiedRow: Link[] = []; + for (let target = 0; target < row.length; target += 1) { + const coefficient = row[target]; + if (!Object.hasOwn(row, target) || !isLink(coefficient)) { + throw new LockInputError(`links[${source}][${target}] должен быть -1, 0 или 1.`); + } + if (source === target && coefficient !== 0) { + throw new LockInputError(`links[${source}][${target}] на диагонали должен быть 0.`); + } + copiedRow.push(coefficient); + } + copiedLinks.push(Object.freeze(copiedRow)); + } + this.state = Object.freeze(copiedState); + this.links = Object.freeze(copiedLinks); + } +} + +export interface Effect { + readonly shifts: readonly { readonly target: number; readonly sign: number }[]; + readonly offset: number; +} + +/** Base-7 encoding and precomputed direct effects belong to one search instance. */ +export class PreparedSearch { + readonly n: number; + readonly size: number; + readonly source: number; + readonly goal: number; + readonly effects: readonly Effect[]; + + constructor(model: LockModel) { + const { state, links } = model; + const n = state.length; + const size = 7 ** n; + // Check before matrix work and even the already-open shortcut. N=18 is safe. + if (!Number.isSafeInteger(size)) { + throw new SearchLimitError('stateEncoding', Number.MAX_SAFE_INTEGER, size); + } + const powers = Array.from({ length: n }, (_, index) => 7 ** (n - index - 1)); + let source = 0; + let goal = 0; + for (let index = 0; index < n; index += 1) { + const power = readAt(powers, index); + source += (readAt(state, index) - 1) * power; + goal += 3 * power; + } + this.n = n; + this.size = size; + this.source = source; + this.goal = goal; + this.effects = links.map((row, plate) => { + const shifts: { target: number; sign: number }[] = []; + let offset = 0; + for (let target = 0; target < n; target += 1) { + const sign = plate === target ? 1 : readAt(row, target); + if (sign === 0) continue; + shifts.push({ target, sign }); + offset += sign * readAt(powers, target); + } + return { shifts, offset }; + }); + } +} + +export function decodeDigits(code: number, digits: Uint8Array): void { + for (let index = digits.length - 1; index >= 0; index -= 1) { + digits[index] = code % 7; + code = Math.floor(code / 7); + } +} + +/** Monotone direct motion makes these endpoint bounds valid for every unit step. */ +export function movementLimits(digits: Uint8Array, effect: Effect): readonly [number, number] { + let plus = 6; + let minus = 6; + for (const { target, sign } of effect.shifts) { + const position = readByte(digits, target); + if (sign === 1) { + if (6 - position < plus) plus = 6 - position; + if (position < minus) minus = position; + } else { + if (position < plus) plus = position; + if (6 - position < minus) minus = 6 - position; + } + } + return [plus, minus]; +} + +function isDelta(value: number): value is Delta { + return Number.isInteger(value) && value !== 0 && value >= -6 && value <= 6; +} + +export function commandFor(plate: number, delta: number): Command { + if (!isDelta(delta)) throw new Error('Internal command displacement invariant failed.'); + return [plate, delta]; +} + +/** + * Checked indexed access for internally dense arrays and bounded state tables. + * Call sites establish bounds through validated dimensions, allocated lengths or + * search membership. The check keeps that invariant executable and avoids type + * assertions / unchecked non-null accesses under noUncheckedIndexedAccess. + */ +export function readAt(values: readonly T[], index: number): T { + const value = values[index]; + if (value === undefined) throw new Error(`Internal indexed-access invariant failed at ${index}.`); + return value; +} + +/** Typed readers keep hot typed-array access separate from object-array shapes. */ +export function readByte(values: Uint8Array, index: number): number { + const value = values[index]; + if (value === undefined) throw new Error(`Internal byte-access invariant failed at ${index}.`); + return value; +} + +export function readInt32(values: Int32Array, index: number): number { + const value = values[index]; + if (value === undefined) throw new Error(`Internal integer-access invariant failed at ${index}.`); + return value; +} diff --git a/src/matrix-analysis.ts b/src/matrix.ts similarity index 97% rename from src/matrix-analysis.ts rename to src/matrix.ts index f14007b..20a2774 100644 --- a/src/matrix-analysis.ts +++ b/src/matrix.ts @@ -1,6 +1,6 @@ import { SearchLimitError } from './errors.ts'; -import { readAt } from './indexed.ts'; -import type { LockModel } from './lock-model.ts'; +import { readAt } from './lock.ts'; +import type { LockModel } from './lock.ts'; interface Rational { readonly n: bigint; diff --git a/src/indexed-heap.ts b/src/priority-queue.ts similarity index 100% rename from src/indexed-heap.ts rename to src/priority-queue.ts diff --git a/src/search-limits.ts b/src/search-limits.ts deleted file mode 100644 index ab8fc1d..0000000 --- a/src/search-limits.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { SearchLimitError } from './errors.ts'; -import { readAt, readByte } from './indexed.ts'; -import type { LockModel } from './lock-model.ts'; -import type { Command, Delta } from './types.ts'; - -export interface SearchLimits { - readonly maxVisited: number; - readonly maxExpanded: number; - readonly maxFrontier: number; - readonly maxDenseBytes: number; -} - -const DEFAULTS: SearchLimits = Object.freeze({ - maxVisited: 2_000_000, - maxExpanded: 1_000_000, - maxFrontier: 1_000_000, - maxDenseBytes: 64 * 1024 * 1024, -}); - -/** Internal budgets are intentionally absent from the public two-argument API. */ -export class SearchBudget { - readonly options: SearchLimits; - private visited = 0; - private expanded = 0; - - constructor(options: Partial = {}) { - this.options = { ...DEFAULTS, ...options }; - } - - check(name: keyof SearchLimits, used: number): void { - if (used > this.options[name]) throw new SearchLimitError(name, this.options[name], used); - } - - visit(): void { - this.check('maxVisited', this.visited + 1); - this.visited += 1; - } - - expand(): void { - this.check('maxExpanded', this.expanded + 1); - this.expanded += 1; - } -} - -export interface Effect { - readonly shifts: readonly { readonly target: number; readonly sign: number }[]; - readonly offset: number; -} - -/** Base-7 encoding and precomputed direct effects belong to one search instance. */ -export class PreparedSearch { - readonly n: number; - readonly size: number; - readonly source: number; - readonly goal: number; - readonly effects: readonly Effect[]; - - constructor(model: LockModel) { - const { state, links } = model; - const n = state.length; - const size = 7 ** n; - // Check before matrix work and even the already-open shortcut. N=18 is safe. - if (!Number.isSafeInteger(size)) { - throw new SearchLimitError('stateEncoding', Number.MAX_SAFE_INTEGER, size); - } - const powers = Array.from({ length: n }, (_, index) => 7 ** (n - index - 1)); - let source = 0; - let goal = 0; - for (let index = 0; index < n; index += 1) { - const power = readAt(powers, index); - source += (readAt(state, index) - 1) * power; - goal += 3 * power; - } - this.n = n; - this.size = size; - this.source = source; - this.goal = goal; - this.effects = links.map((row, plate) => { - const shifts: { target: number; sign: number }[] = []; - let offset = 0; - for (let target = 0; target < n; target += 1) { - const sign = plate === target ? 1 : readAt(row, target); - if (sign === 0) continue; - shifts.push({ target, sign }); - offset += sign * readAt(powers, target); - } - return { shifts, offset }; - }); - } -} - -export function decodeDigits(code: number, digits: Uint8Array): void { - for (let index = digits.length - 1; index >= 0; index -= 1) { - digits[index] = code % 7; - code = Math.floor(code / 7); - } -} - -/** Monotone direct motion makes these endpoint bounds valid for every unit step. */ -export function movementLimits(digits: Uint8Array, effect: Effect): readonly [number, number] { - let plus = 6; - let minus = 6; - for (const { target, sign } of effect.shifts) { - const position = readByte(digits, target); - if (sign === 1) { - if (6 - position < plus) plus = 6 - position; - if (position < minus) minus = position; - } else { - if (position < plus) plus = position; - if (6 - position < minus) minus = 6 - position; - } - } - return [plus, minus]; -} - -function isDelta(value: number): value is Delta { - return Number.isInteger(value) && value !== 0 && value >= -6 && value <= 6; -} - -export function commandFor(plate: number, delta: number): Command { - if (!isDelta(delta)) throw new Error('Internal command displacement invariant failed.'); - return [plate, delta]; -} diff --git a/src/types.ts b/src/types.ts index fe44fab..92ae08b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,7 +10,3 @@ export type Command = readonly [index: number, delta: Delta]; export type Links = { readonly [Row in keyof S]: { readonly [Column in keyof S]: Link }; }; - -export interface SearchStrategy { - solve(): readonly Command[] | null; -} diff --git a/tests/benchmarks/benchmark.ts b/tests/benchmarks/benchmark.ts new file mode 100644 index 0000000..43f2748 --- /dev/null +++ b/tests/benchmarks/benchmark.ts @@ -0,0 +1,2 @@ +import { main } from './cli.ts'; +try { await main(); } catch (error) { console.error(error); process.exitCode = 1; } diff --git a/tests/benchmarks/catalog.ts b/tests/benchmarks/catalog.ts new file mode 100644 index 0000000..851391c --- /dev/null +++ b/tests/benchmarks/catalog.ts @@ -0,0 +1,43 @@ +import { createHash } from 'node:crypto'; +import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { relative, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { array, integer, record, string, type Definition, type Metrics } from './validation.ts'; + +export const catalogSha256 = '902a57e0c867b7ed7513af09257fde765396593ab45738bb60967b412b4bcd69'; +export type Fixture = Definition & { id: string; expectedActions: number }; +export function sha256(value: string | Uint8Array): string { return createHash('sha256').update(value).digest('hex'); } +export function jsonFile(path: string | URL): unknown { const parsed: unknown = JSON.parse(readFileSync(path, 'utf8')); return parsed; } +export function readMetrics(value: unknown): Metrics { + const item = record(value, 'metrics'); + return { A: integer(item['A']), U: integer(item['U']), C: integer(item['C']), plateSwitches: integer(item['plateSwitches']) }; +} +export function loadCatalog(): Fixture[] { + const bytes = readFileSync(new URL('./fixtures.json', import.meta.url)); + if (sha256(bytes) !== catalogSha256) throw new Error('Fixture hash differs from the fixed 45-input pin.'); + const parsed: unknown = JSON.parse(bytes.toString('utf8')); + const locks = array(parsed, 'fixtures').map((raw, index): Fixture => { + const lock = record(raw, 'fixture'); + if (Object.keys(lock).sort().join(',') !== 'expectedActions,id,links,state') throw new Error('Unexpected fixture fields.'); + const id = string(lock['id']); + const state = array(lock['state']).map((value) => integer(value)); + const links = array(lock['links']).map((row) => array(row).map((value) => integer(value))); + const expectedActions = integer(lock['expectedActions']); + if (id !== `lock-${String(index + 1).padStart(3, '0')}` || expectedActions < 0 || state.length === 0 + || state.some((value) => value < 1 || value > 7) || links.length !== state.length + || links.some((row) => row.length !== state.length || row.some((value) => value < -1 || value > 1))) throw new Error('Invalid fixed fixture.'); + return { id, state, links, expectedActions }; + }); + if (locks.length !== 45) throw new Error('Expected 45 unique fixed inputs.'); + return locks; +} +export function git(directory: string, args: readonly string[]): string { + return execFileSync('git', ['-C', directory, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); +} + +export function assertArtifactOutput(path: string): void { + const root = fileURLToPath(new URL('../../', import.meta.url)); + const local = relative(root, resolve(path)); + if (local !== 'artifacts' && !local.startsWith('artifacts/')) throw new Error('Benchmark output must be under the ignored artifacts directory.'); +} diff --git a/tests/benchmarks/cli.ts b/tests/benchmarks/cli.ts new file mode 100644 index 0000000..435b1bb --- /dev/null +++ b/tests/benchmarks/cli.ts @@ -0,0 +1,93 @@ +import { readFileSync, statSync } from 'node:fs'; +import os from 'node:os'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { assertArtifactOutput, catalogSha256, git, loadCatalog, sha256 } from './catalog.ts'; +import { loadImplementation, publishReport, readSnapshot, runComparison, type ModuleFormat, type Snapshot } from './harness.ts'; +import { collectMemory, type MemorySpec } from './memory.ts'; +import { combineVerdicts } from './comparison.ts'; +import { generateSnapshot } from './snapshot.ts'; +import { hashFiles, inspectComparisonBaseline, inspectSource, revision } from './source.ts'; + +const root = fileURLToPath(new URL('../../', import.meta.url)); +export type BenchmarkOptions = { candidateModule: string; baselineModule: string; baselineSnapshot?: string; + baselineFormat: ModuleFormat; outputDir: string; warmups: number; repetitions: number; + memoryRepetitions: number; smoke: boolean; skipMemory: boolean; help: boolean }; +export function parseBenchmarkArgs(args: readonly string[]): BenchmarkOptions { + const options: BenchmarkOptions = { candidateModule: join(root, 'dist/gothic-lock-solver.mjs'), baselineModule: '', baselineFormat: 'tuple', + outputDir: join(root, 'artifacts/benchmark'), warmups: 2, repetitions: 7, memoryRepetitions: 5, smoke: false, skipMemory: false, help: false }; + const valued = ['--candidate-module', '--baseline-module', '--baseline-snapshot', '--baseline-format', '--output-dir', '--warmups', '--repetitions', '--memory-repetitions']; + const seen = new Set(); + for (let index = 0; index < args.length; index += 1) { + const argument = args[index]; if (argument === undefined) throw new Error('Missing argument.'); + if (seen.has(argument)) throw new Error(`Duplicate argument: ${argument}`); seen.add(argument); + if (argument === '--smoke') { options.smoke = true; continue; } + if (argument === '--skip-memory') { options.skipMemory = true; continue; } + if (argument === '--help') { options.help = true; continue; } + if (!valued.includes(argument)) throw new Error(`Unknown argument: ${argument}`); + const value = args[++index]; if (value === undefined || value === '' || value.startsWith('--')) throw new Error(`Missing argument value: ${argument}`); + switch (argument) { + case '--candidate-module': options.candidateModule = resolve(value); break; + case '--baseline-module': options.baselineModule = resolve(value); break; + case '--baseline-snapshot': options.baselineSnapshot = resolve(value); break; + case '--baseline-format': if (value !== 'tuple' && value !== 'legacy') throw new Error('Baseline format must be tuple or legacy.'); options.baselineFormat = value; break; + case '--output-dir': options.outputDir = resolve(value); break; + case '--warmups': case '--repetitions': case '--memory-repetitions': { + if (!/^(0|[1-9][0-9]*)$/.test(value)) throw new Error('Expected a nonnegative integer.'); + const count = Number(value); const minimum = argument === '--warmups' ? 0 : 1; + if (!Number.isSafeInteger(count) || count < minimum) throw new Error('Invalid repetition/warmup integer.'); + if (argument === '--warmups') options.warmups = count; + else if (argument === '--repetitions') options.repetitions = count; + else options.memoryRepetitions = count; + break; + } + } + } + if (!options.help && !seen.has('--baseline-module')) throw new Error('Benchmark requires explicit --baseline-module from an own target checkout or verified previous release.'); + return options; +} +export { assertArtifactOutput } from './catalog.ts'; +export function collectMetadata(options: BenchmarkOptions, baselineSnapshot: Snapshot) { + const baselineSource = inspectComparisonBaseline(options.candidateModule, options.baselineModule, options.baselineFormat, baselineSnapshot.source); + const runtimeFiles = ['gothic-lock-solver.mjs', 'gothic-lock-solver.min.mjs', 'gothic-lock-solver.js', 'gothic-lock-solver.min.js', 'gothic-lock-solver.cli.mjs']; + const artifacts = Object.fromEntries(runtimeFiles.map((name) => { + const path = join(root, 'dist', name); return [name, { bytes: statSync(path).size, sha256: sha256(readFileSync(path)) }]; + })); + return { environment: { node: process.version, v8: process.versions.v8, platform: process.platform, arch: process.arch, + cpu: os.cpus()[0]?.model ?? 'unknown', logicalCpus: os.cpus().length, osRelease: os.release(), totalMemoryBytes: os.totalmem(), execArgv: process.execArgv }, + source: { revision: revision(root), dirty: git(root, ['status', '--porcelain', '--untracked-files=normal']).length > 0, + files: hashFiles(join(root, 'src'), (path) => path.endsWith('.ts')), harness: hashFiles(join(root, 'tests/benchmarks'), (path) => path.endsWith('.ts')) }, + candidate: { revision: revision(dirname(options.candidateModule)), moduleSha256: sha256(readFileSync(options.candidateModule)), format: 'tuple', module: options.candidateModule }, + baseline: { ...baselineSource, module: options.baselineModule, + snapshotSha256: sha256(`${JSON.stringify(baselineSnapshot, null, 2)}\n`), snapshot: options.baselineSnapshot ?? 'baseline-snapshot.json', kind: baselineSource['kind'] ?? 'own-target-checkout' }, + fixtures: { count: 45, catalogSha256 }, artifacts }; +} +export async function main(): Promise { + const options = parseBenchmarkArgs(process.argv.slice(2)); + if (options.help) { + console.log('Usage: pnpm benchmark --baseline-module PATH [--baseline-format tuple|legacy] [--baseline-snapshot PATH] [--output-dir artifacts/benchmark] [--warmups 2] [--repetitions 7] [--memory-repetitions 5] [--smoke] [--skip-memory]'); + console.log('The baseline snapshot is generated from the supplied own checkout when omitted. A verified previous release supplies its module and snapshot. Candidate defaults to the built ESM; --candidate-module PATH overrides it.'); + return; + } + assertArtifactOutput(options.outputDir); + const stored = options.baselineSnapshot === undefined ? undefined : readSnapshot(options.baselineSnapshot); + inspectComparisonBaseline(options.candidateModule, options.baselineModule, options.baselineFormat, stored?.source); + const snapshot = stored ?? await generateSnapshot(options.baselineModule, options.baselineFormat, 'Fresh execution of the supplied own baseline on the fixed inputs.'); + const metadata = collectMetadata(options, snapshot); + const candidate = await loadImplementation(options.candidateModule, 'tuple', 'Candidate TypeScript library'); + const baseline = await loadImplementation(options.baselineModule, options.baselineFormat, 'Own target or preceding-release baseline'); + const memorySpecs: MemorySpec[] = [ { name: 'candidate', module: options.candidateModule, format: 'tuple' }, + { name: 'baseline', module: options.baselineModule, format: options.baselineFormat } ]; + const catalog = loadCatalog(); const locks = options.smoke ? catalog.slice(0, 2) : catalog; + const report = runComparison({ locks, candidate, baseline, baselineSnapshot: snapshot, repetitions: options.repetitions, warmups: options.warmups, + onProgress: (completed, total, id) => process.stderr.write(`[${completed}/${total}] ${id}: replay and optimum checked\n`) }); + const memory = options.skipMemory ? { performance: { verdict: 'inconclusive' as const }, available: false, reason: 'Memory explicitly skipped; no memory pass is established.', browser: { available: false } } + : collectMemory(locks, memorySpecs, options.memoryRepetitions, (line) => process.stderr.write(`${line}\n`)); + inspectSource(options.baselineModule, options.baselineFormat, snapshot.source); + if (sha256(readFileSync(options.candidateModule)) !== metadata.candidate.moduleSha256) throw new Error('Candidate source changed during measurement.'); + report.performance = combineVerdicts([report.performance, memory.performance.verdict]); + const paths = await publishReport(report, options.outputDir, metadata, memory, snapshot); + console.log(JSON.stringify({ ...paths, lockCount: locks.length, quality: report.quality, performance: report.performance }, null, 2)); + // Uncertain observations remain evidence for review; deterministic failures and confirmed regressions fail CI. + if (report.quality === 'regression' || report.performance === 'regression') process.exitCode = 1; +} diff --git a/tests/benchmarks/compare.ts b/tests/benchmarks/compare.ts new file mode 100644 index 0000000..43f2748 --- /dev/null +++ b/tests/benchmarks/compare.ts @@ -0,0 +1,2 @@ +import { main } from './cli.ts'; +try { await main(); } catch (error) { console.error(error); process.exitCode = 1; } diff --git a/benchmarks/comparison.ts b/tests/benchmarks/comparison.ts similarity index 100% rename from benchmarks/comparison.ts rename to tests/benchmarks/comparison.ts diff --git a/tests/benchmarks/fixtures.json b/tests/benchmarks/fixtures.json new file mode 100644 index 0000000..830d6fc --- /dev/null +++ b/tests/benchmarks/fixtures.json @@ -0,0 +1,2580 @@ +[ + { + "id": "lock-001", + "state": [ + 5, + 6, + 2, + 2, + 1, + 1 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + -1, + 1, + 1, + 0 + ], + [ + 0, + 0, + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + -1, + 1, + -1, + 0, + 0, + -1 + ], + [ + -1, + 1, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 17 + }, + { + "id": "lock-002", + "state": [ + 6, + 3, + 2, + 1, + 7 + ], + "links": [ + [ + 0, + 0, + 0, + 1, + 0 + ], + [ + 0, + 0, + 0, + 0, + 1 + ], + [ + 0, + 0, + 0, + 0, + -1 + ], + [ + 0, + 1, + -1, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 9 + }, + { + "id": "lock-003", + "state": [ + 7, + 7, + 6, + 1 + ], + "links": [ + [ + 0, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 1 + ], + [ + 1, + -1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 5 + }, + { + "id": "lock-004", + "state": [ + 6, + 6, + 5, + 7, + 1, + 4 + ], + "links": [ + [ + 0, + 0, + 1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + -1, + 0, + 0, + -1, + 0 + ], + [ + -1, + 0, + 1, + 0, + 0, + 0 + ], + [ + 0, + -1, + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 14 + }, + { + "id": "lock-005", + "state": [ + 1, + 5, + 1, + 7, + 2, + 6 + ], + "links": [ + [ + 0, + 0, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + -1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + -1, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + -1 + ], + [ + 0, + -1, + 0, + 0, + 1, + 0 + ] + ], + "expectedActions": 11 + }, + { + "id": "lock-006", + "state": [ + 3, + 7, + 2, + 6, + 5 + ], + "links": [ + [ + 0, + -1, + -1, + -1, + -1 + ], + [ + -1, + 0, + 0, + 0, + 0 + ], + [ + -1, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + 0 + ], + [ + 1, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 9 + }, + { + "id": "lock-007", + "state": [ + 2, + 1, + 5, + 4, + 3, + 1 + ], + "links": [ + [ + 0, + 0, + 0, + -1, + 0, + 0 + ], + [ + -1, + 0, + 1, + -1, + -1, + 1 + ], + [ + 0, + 1, + 0, + 0, + 0, + 1 + ], + [ + 0, + 0, + 0, + 0, + 0, + -1 + ], + [ + 0, + -1, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 9 + }, + { + "id": "lock-008", + "state": [ + 1, + 3, + 1, + 1 + ], + "links": [ + [ + 0, + 0, + 0, + -1 + ], + [ + -1, + 0, + 1, + 1 + ], + [ + -1, + -1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 5 + }, + { + "id": "lock-009", + "state": [ + 1, + 4, + 1, + 5, + 7, + 1 + ], + "links": [ + [ + 0, + -1, + 0, + 0, + 0, + 0 + ], + [ + -1, + 0, + -1, + 0, + -1, + 0 + ], + [ + -1, + 0, + 0, + 0, + 0, + -1 + ], + [ + 0, + 0, + 1, + 0, + 0, + 0 + ], + [ + 0, + 1, + -1, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 17 + }, + { + "id": "lock-010", + "state": [ + 1, + 1, + 3, + 1, + 7, + 6, + 7 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 0, + -1, + 0 + ], + [ + 0, + 0, + 1, + 0, + -1, + 0, + 1 + ], + [ + 0, + 0, + 0, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + -1, + 0, + 0 + ], + [ + 0, + 0, + 0, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 1, + 0, + 0, + 0 + ], + [ + -1, + 0, + -1, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 7 + }, + { + "id": "lock-011", + "state": [ + 7, + 3, + 2, + 6, + 2, + 6 + ], + "links": [ + [ + 0, + -1, + -1, + -1, + -1, + -1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0, + 1, + -1 + ], + [ + 0, + 0, + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 0, + -1, + 0, + 0 + ] + ], + "expectedActions": 9 + }, + { + "id": "lock-012", + "state": [ + 6, + 1, + 2, + 1, + 7 + ], + "links": [ + [ + 0, + -1, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0, + 1 + ], + [ + -1, + -1, + 0, + -1, + 1 + ], + [ + 0, + 0, + 0, + 0, + 0 + ], + [ + 1, + 0, + 1, + 0, + 0 + ] + ], + "expectedActions": 23 + }, + { + "id": "lock-013", + "state": [ + 3, + 1, + 7, + 1, + 2, + 2 + ], + "links": [ + [ + 0, + 0, + 0, + -1, + -1, + -1 + ], + [ + -1, + 0, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + -1, + 1, + 0 + ], + [ + 0, + 0, + 1, + 0, + 0, + 0 + ], + [ + 0, + -1, + 1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 20 + }, + { + "id": "lock-014", + "state": [ + 7, + 6, + 4, + 7, + 2, + 7, + 5 + ], + "links": [ + [ + 0, + 0, + 0, + -1, + 0, + -1, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 1, + 0, + -1, + 1, + 0, + 1, + -1 + ], + [ + 0, + 0, + 0, + -1, + 1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 9 + }, + { + "id": "lock-015", + "state": [ + 7, + 7, + 7, + 7, + 2 + ], + "links": [ + [ + 0, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0 + ], + [ + -1, + -1, + 0, + 1, + 1 + ], + [ + -1, + 1, + 0, + 0, + 0 + ], + [ + 0, + -1, + 0, + 1, + 0 + ] + ], + "expectedActions": 7 + }, + { + "id": "lock-016", + "state": [ + 4, + 3, + 3, + 2, + 1, + 6 + ], + "links": [ + [ + 0, + 0, + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + -1, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 1 + ], + [ + 0, + -1, + 0, + -1, + 0, + 0 + ], + [ + 0, + 0, + 0, + -1, + 1, + 0 + ] + ], + "expectedActions": 11 + }, + { + "id": "lock-017", + "state": [ + 2, + 6, + 5, + 6, + 5, + 7 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 1, + 0, + 0, + 0, + 1, + -1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + -1, + 0, + 0, + 0 + ], + [ + 1, + -1, + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 1, + -1, + 0 + ] + ], + "expectedActions": 13 + }, + { + "id": "lock-018", + "state": [ + 1, + 2, + 7, + 7, + 6, + 5 + ], + "links": [ + [ + 0, + -1, + -1, + 0, + -1, + 0 + ], + [ + 0, + 0, + 0, + 1, + 0, + 1 + ], + [ + 0, + 1, + 0, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + -1, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + 1, + 0 + ] + ], + "expectedActions": 14 + }, + { + "id": "lock-019", + "state": [ + 4, + 5, + 7, + 7, + 7 + ], + "links": [ + [ + 0, + 0, + 0, + -1, + 0 + ], + [ + -1, + 0, + -1, + 1, + 0 + ], + [ + 0, + -1, + 0, + 1, + -1 + ], + [ + 0, + -1, + 0, + 0, + -1 + ], + [ + 0, + 0, + -1, + 0, + 0 + ] + ], + "expectedActions": 14 + }, + { + "id": "lock-020", + "state": [ + 3, + 6, + 3, + 7, + 7, + 7 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 1, + 0, + 0, + 0, + -1, + -1 + ], + [ + 0, + 1, + 0, + 0, + 0, + 0 + ], + [ + 1, + -1, + 0, + 0, + 0, + 0 + ], + [ + 1, + 0, + 0, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 12 + }, + { + "id": "lock-021", + "state": [ + 7, + 6, + 4, + 5, + 5, + 4 + ], + "links": [ + [ + 0, + 0, + 0, + -1, + -1, + 1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 1 + ], + [ + -1, + 1, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 1, + 1, + -1, + 0, + 0, + 0 + ] + ], + "expectedActions": 11 + }, + { + "id": "lock-022", + "state": [ + 7, + 2, + 2, + 1, + 3, + 7 + ], + "links": [ + [ + 0, + 0, + -1, + 0, + -1, + -1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0, + 1, + 1 + ], + [ + 1, + 0, + 0, + 0, + -1, + 1 + ], + [ + 0, + 0, + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 8 + }, + { + "id": "lock-023", + "state": [ + 2, + 3, + 5, + 7, + 6, + 1 + ], + "links": [ + [ + 0, + 1, + 1, + 1, + 0, + 0 + ], + [ + -1, + 0, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + -1, + 0 + ], + [ + -1, + 0, + 0, + -1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 16 + }, + { + "id": "lock-024", + "state": [ + 2, + 7, + 5, + 6, + 7 + ], + "links": [ + [ + 0, + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + 0 + ], + [ + -1, + 0, + 0, + 0, + 1 + ], + [ + 0, + 0, + 1, + 0, + 0 + ], + [ + -1, + -1, + 1, + 0, + 0 + ] + ], + "expectedActions": 8 + }, + { + "id": "lock-025", + "state": [ + 2, + 2, + 6, + 5, + 7 + ], + "links": [ + [ + 0, + -1, + 1, + 0, + -1 + ], + [ + 1, + 0, + 1, + 1, + 0 + ], + [ + 0, + 0, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0, + 0 + ] + ], + "expectedActions": 14 + }, + { + "id": "lock-026", + "state": [ + 3, + 7, + 1, + 3, + 1 + ], + "links": [ + [ + 0, + 0, + 0, + -1, + 0 + ], + [ + -1, + 0, + 1, + -1, + 1 + ], + [ + 1, + 0, + 0, + -1, + -1 + ], + [ + 0, + 0, + 0, + 0, + 1 + ], + [ + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 9 + }, + { + "id": "lock-027", + "state": [ + 4, + 6, + 6, + 7, + 1, + 7, + 4 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + -1, + -1, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + -1, + 0, + 0, + 0, + 0, + 0 + ], + [ + -1, + 0, + 1, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 1, + 1, + 0, + 0 + ], + [ + -1, + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 8 + }, + { + "id": "lock-028", + "state": [ + 5, + 7, + 2, + 3, + 5, + 7 + ], + "links": [ + [ + 0, + 0, + -1, + 0, + -1, + 0 + ], + [ + 0, + 0, + 0, + 1, + 0, + 0 + ], + [ + -1, + 0, + 0, + 0, + 0, + 0 + ], + [ + -1, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + -1, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + 0, + 0 + ] + ], + "expectedActions": 11 + }, + { + "id": "lock-029", + "state": [ + 5, + 4, + 1, + 1, + 6, + 2 + ], + "links": [ + [ + 0, + -1, + -1, + 1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + -1, + 0, + 0 + ], + [ + 1, + 0, + 0, + 0, + 1, + 1 + ], + [ + -1, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 20 + }, + { + "id": "lock-030", + "state": [ + 5, + 1, + 7, + 7, + 6, + 4 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 1, + -1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 1 + ], + [ + -1, + 0, + 0, + 0, + 0, + 0 + ], + [ + -1, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + -1 + ], + [ + 0, + 0, + -1, + 0, + 0, + 0 + ] + ], + "expectedActions": 10 + }, + { + "id": "lock-031", + "state": [ + 4, + 3, + 7, + 2, + 5 + ], + "links": [ + [ + 0, + -1, + -1, + 0, + 0 + ], + [ + 1, + 0, + 0, + -1, + 0 + ], + [ + -1, + 1, + 0, + 1, + 0 + ], + [ + -1, + 0, + 0, + 0, + 1 + ], + [ + 0, + 0, + -1, + 0, + 0 + ] + ], + "expectedActions": 9 + }, + { + "id": "lock-032", + "state": [ + 2, + 4, + 4, + 6, + 2, + 7 + ], + "links": [ + [ + 0, + 1, + 0, + 0, + 0, + 1 + ], + [ + 0, + 0, + 1, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + 0, + 1 + ], + [ + 0, + 0, + -1, + 0, + 0, + 0 + ] + ], + "expectedActions": 9 + }, + { + "id": "lock-033", + "state": [ + 5, + 5, + 6, + 2, + 7, + 7 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + 0, + -1 + ], + [ + 0, + -1, + 0, + 0, + 1, + -1 + ], + [ + 1, + 0, + -1, + 0, + 0, + 0 + ], + [ + -1, + 0, + 0, + 0, + 0, + -1 + ], + [ + 0, + 0, + -1, + 0, + 0, + 0 + ] + ], + "expectedActions": 10 + }, + { + "id": "lock-034", + "state": [ + 5, + 1, + 7, + 1, + 1, + 3 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 1, + 0, + 0, + -1, + 0, + 1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 1 + ], + [ + 0, + 0, + 1, + -1, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 8 + }, + { + "id": "lock-035", + "state": [ + 7, + 2, + 2, + 6, + 1, + 3 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 1, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 1, + 0, + 0, + 0, + -1, + 1 + ], + [ + 0, + 0, + 0, + 0, + 1, + 0 + ], + [ + 0, + -1, + 0, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + -1, + 0, + 0 + ] + ], + "expectedActions": 10 + }, + { + "id": "lock-036", + "state": [ + 6, + 2, + 1, + 1, + 5 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + 1 + ], + [ + -1, + 0, + 0, + 0, + 1 + ], + [ + 0, + 0, + -1, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0, + 0 + ] + ], + "expectedActions": 8 + }, + { + "id": "lock-037", + "state": [ + 2, + 1, + 6, + 5, + 4, + 7 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + -1, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 1, + 0, + 0, + -1, + 0 + ], + [ + 0, + -1, + 1, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0, + 1, + 0 + ] + ], + "expectedActions": 11 + }, + { + "id": "lock-038", + "state": [ + 5, + 2, + 4, + 1, + 4, + 5 + ], + "links": [ + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 1, + -1, + 0 + ], + [ + 1, + -1, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + -1, + -1, + 1, + -1, + 0 + ] + ], + "expectedActions": 8 + }, + { + "id": "lock-039", + "state": [ + 3, + 1, + 2, + 7, + 2, + 1 + ], + "links": [ + [ + 0, + 0, + 0, + 1, + 0, + 1 + ], + [ + 1, + 0, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 1, + 0, + 0 + ] + ], + "expectedActions": 8 + }, + { + "id": "lock-040", + "state": [ + 7, + 1, + 2, + 2, + 5 + ], + "links": [ + [ + 0, + 0, + 0, + -1, + 1 + ], + [ + 1, + 0, + -1, + 0, + -1 + ], + [ + 1, + -1, + 0, + 0, + 0 + ], + [ + 1, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + -1, + 0 + ] + ], + "expectedActions": 13 + }, + { + "id": "lock-041", + "state": [ + 1, + 3, + 7, + 6, + 5 + ], + "links": [ + [ + 0, + 0, + 0, + -1, + 0 + ], + [ + 0, + 0, + 0, + -1, + 0 + ], + [ + 1, + 0, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0, + 1 + ], + [ + 1, + 1, + 0, + 0, + 0 + ] + ], + "expectedActions": 9 + }, + { + "id": "lock-042", + "state": [ + 1, + 2, + 1, + 1 + ], + "links": [ + [ + 0, + 0, + 0, + -1 + ], + [ + 0, + 0, + 0, + 0 + ], + [ + 0, + -1, + 0, + 0 + ], + [ + 1, + -1, + -1, + 0 + ] + ], + "expectedActions": 4 + }, + { + "id": "lock-043", + "state": [ + 1, + 7, + 4, + 6, + 4 + ], + "links": [ + [ + 0, + -1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + -1 + ], + [ + -1, + 0, + 0, + 0, + 0 + ], + [ + 1, + 1, + 1, + 0, + -1 + ], + [ + 0, + -1, + -1, + -1, + 0 + ] + ], + "expectedActions": 5 + }, + { + "id": "lock-044", + "state": [ + 2, + 4, + 5, + 7, + 5, + 5 + ], + "links": [ + [ + 0, + -1, + 1, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 1, + 0, + 0, + 0, + -1, + 0 + ], + [ + 0, + -1, + -1, + 0, + 0, + 1 + ], + [ + 0, + 0, + 0, + -1, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 11 + }, + { + "id": "lock-045", + "state": [ + 1, + 4, + 5, + 2, + 1, + 4 + ], + "links": [ + [ + 0, + 0, + 1, + 0, + -1, + 0 + ], + [ + 0, + 0, + -1, + -1, + 0, + 0 + ], + [ + 0, + 0, + 0, + 1, + 0, + 1 + ], + [ + -1, + 0, + -1, + 0, + 0, + 1 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0 + ] + ], + "expectedActions": 10 + } +] diff --git a/benchmarks/harness.ts b/tests/benchmarks/harness.ts similarity index 79% rename from benchmarks/harness.ts rename to tests/benchmarks/harness.ts index b47ed73..eb15ffa 100644 --- a/benchmarks/harness.ts +++ b/tests/benchmarks/harness.ts @@ -1,17 +1,17 @@ import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import { mkdir, mkdtemp, rename, rm, writeFile } from 'node:fs/promises'; -import { readFileSync } from 'node:fs'; import { join, resolve } from 'node:path'; import { performance } from 'node:perf_hooks'; import { pathToFileURL } from 'node:url'; -import { array, commandMetrics, groupReferenceClicks, legacyCommands, record, replayCommands, string, tupleCommands, type Definition, type Metrics, type TupleCommand } from './validation.ts'; -import { catalogSha256, git, jsonFile, loadCatalog, readMetrics, sha256, upstreamPin, verifyReferenceSource, type Fixture } from './catalog.ts'; +import { array, commandMetrics, legacyCommands, record, replayCommands, string, tupleCommands, type Definition, type Metrics, type TupleCommand } from './validation.ts'; +import { catalogSha256, jsonFile, loadCatalog, readMetrics, sha256, type Fixture } from './catalog.ts'; +import { validateSource } from './source.ts'; import { combineVerdicts, compareMetrics, confirmPerformance, performanceVerdict, statistics } from './comparison.ts'; export type ModuleFormat = 'tuple' | 'legacy'; export type Implementation = { label: string; solve: (definition: Definition) => unknown; - normalize: (raw: unknown) => unknown; requiresOptimality: boolean; expectedPath?: 'bfs' | 'upstream' }; + normalize: (raw: unknown) => unknown }; export type Solution = Metrics & { commands: readonly TupleCommand[] }; export type Snapshot = { schemaVersion: number; fixtureSha256: string; source: Record; reason: string; locks: (Solution & { id: string; optimumA: number })[] }; @@ -21,7 +21,7 @@ export async function loadImplementation(modulePath: string, format: ModuleForma const module: unknown = await import(pathToFileURL(resolve(modulePath)).href); const exported = record(module, 'solver module')['solveLock']; if (typeof exported !== 'function') throw new Error(`Missing named solveLock export: ${modulePath}`); - return { label, requiresOptimality: true, + return { label, solve: (definition) => { const raw: unknown = format === 'tuple' ? exported(definition.state, definition.links) : exported(definition); return raw; }, normalize: (raw) => { if (format === 'tuple') return raw; @@ -31,18 +31,9 @@ export async function loadImplementation(modulePath: string, format: ModuleForma return legacyCommands(result['commands']); } }; } -export function loadUpstream(directory: string): Implementation { - const pin = upstreamPin(); - const html = readFileSync(join(directory, pin.solver.path), 'utf8'); - const block = verifyReferenceSource({ revision: git(directory, ['rev-parse', 'HEAD']), html }, pin); - // Executes only the independently hash-verified external source, never copied into this repository. - const exported: unknown = Function(`${block}\n; return solveLock;`)(); - if (typeof exported !== 'function') throw new Error('Pinned upstream solveLock is missing.'); - return { label: 'UnlockMyLoot pinned external reference', requiresOptimality: false, expectedPath: 'upstream', - solve: (definition) => { const raw: unknown = exported(definition.state, definition.links); return raw; }, normalize: groupReferenceClicks }; -} export function readSnapshot(path: string): Snapshot { const value = record(jsonFile(path), 'snapshot'); + const source = validateSource(value['source']); if (value['schemaVersion'] !== 1 || value['fixtureSha256'] !== catalogSha256) throw new Error('Snapshot schema or fixture hash mismatch.'); const locks = array(value['locks'], 'snapshot locks').map((raw) => { const row = record(raw); const metrics = readMetrics(row); const commands = tupleCommands(row['commands']); @@ -51,48 +42,43 @@ export function readSnapshot(path: string): Snapshot { return { id: string(row['id']), optimumA: row['optimumA'], ...metrics, commands }; }); if (locks.length !== 45 || new Set(locks.map((lock) => lock.id)).size !== 45) throw new Error('Snapshot requires 45 unique locks.'); - return { schemaVersion: 1, fixtureSha256: catalogSha256, source: record(value['source']), reason: string(value['reason']), locks }; + for (const fixture of loadCatalog()) { + const row = locks.find((lock) => lock.id === fixture.id); + if (row === undefined || row.optimumA !== fixture.expectedActions) throw new Error('Snapshot fixture identity or optimum mismatch.'); + validateSolution(fixture, row.commands); + } + return { schemaVersion: 1, fixtureSha256: catalogSha256, source, reason: string(value['reason']), locks }; } export async function writeSnapshot(path: string, snapshot: unknown): Promise { // Deliberately exclusive. Refreshes require a new reviewed destination, never an implicit replacement. await writeFile(path, `${JSON.stringify(snapshot, null, 2)}\n`, { flag: 'wx' }); } -function validate(lock: Fixture, implementation: Implementation, raw: unknown): Solution { +export function validateSolution(lock: Fixture, raw: unknown): Solution { if (raw === null) throw new Error(`${lock.id}: lost solvability.`); const commands = tupleCommands(raw); - assert.deepEqual(replayCommands(lock.definition, commands), lock.definition.state.map(() => 4), `${lock.id}: target not reached.`); + assert.deepEqual(replayCommands(lock, commands), lock.state.map(() => 4), `${lock.id}: target not reached.`); const metrics = commandMetrics(commands); - if (implementation.requiresOptimality) assert.equal(metrics.A, lock.expectedActions, `${lock.id}: wrong exact action minimum.`); - else assert.ok(metrics.A >= lock.expectedActions, `${lock.id}: upstream beats claimed optimum.`); - if (implementation.expectedPath === 'bfs') assert.deepEqual(commands, lock.baselineCommands, `${lock.id}: pinned BFS path changed.`); - if (implementation.expectedPath === 'upstream') { - assert.deepEqual(commands, lock.historicalReference.commands, `${lock.id}: pinned upstream path drift.`); - assert.deepEqual(metrics, commandMetrics(lock.historicalReference.commands), `${lock.id}: upstream metrics drift.`); - } + assert.equal(metrics.A, lock.expectedActions, `${lock.id}: wrong exact action minimum.`); return { ...metrics, commands }; } function measure(lock: Fixture, implementation: Implementation, clock: () => number): { solution: Solution; elapsed: number } { - const definition = structuredClone(lock.definition); const before = JSON.stringify(definition); + const definition = structuredClone({ state: lock.state, links: lock.links }); const before = JSON.stringify(definition); const start = clock(); const raw = implementation.solve(definition); const elapsed = clock() - start; assert.equal(JSON.stringify(definition), before, `${lock.id}: solver mutated its input.`); - return { solution: validate(lock, implementation, implementation.normalize(raw)), elapsed }; + return { solution: validateSolution(lock, implementation.normalize(raw)), elapsed }; } export function runComparison(options: { locks?: Fixture[]; candidate: Implementation; baseline: Implementation; baselineSnapshot?: Snapshot; - others?: Record; repetitions?: number; warmups?: number; clock?: () => number; + repetitions?: number; warmups?: number; clock?: () => number; onProgress?: (completed: number, total: number, id: string) => void; }) { const locks = options.locks ?? loadCatalog(); const repetitions = options.repetitions ?? 7; const warmups = options.warmups ?? 2; const clock = options.clock ?? (() => performance.now()); if (!Number.isSafeInteger(repetitions) || repetitions < 1 || !Number.isSafeInteger(warmups) || warmups < 0) throw new Error('Invalid repetition/warmup count.'); if (locks.length === 0 || new Set(locks.map((lock) => lock.id)).size !== locks.length) throw new Error('Expected unique nonempty fixture set.'); - const implementations = { ...options.others, candidate: options.candidate, baseline: options.baseline }; + const implementations = { candidate: options.candidate, baseline: options.baseline }; const entries = Object.entries(implementations); const results = locks.map((lock, lockIndex) => { - assert.deepEqual(replayCommands(lock.definition, lock.baselineCommands), lock.definition.state.map(() => 4)); - assert.equal(lock.baselineCommands.length, lock.expectedActions, 'Pinned BFS optimum mismatch.'); - assert.deepEqual(replayCommands(lock.definition, lock.historicalReference.commands), lock.definition.state.map(() => 4)); - assert.deepEqual(commandMetrics(lock.historicalReference.commands), readMetrics(lock.historicalReference)); const selfA: number[] = []; const selfB: number[] = []; let baselineControl: Solution | undefined; for (let round = -warmups; round < repetitions; round += 1) { @@ -155,9 +141,8 @@ export function runComparison(options: { const timing = { ...firstTiming, verdict: repeatTiming === undefined ? firstTiming.verdict : confirmPerformance(firstTiming.verdict, repeatTiming.verdict), reason: repeatTiming === undefined ? firstTiming.reason : `First: ${firstTiming.verdict}; bounded repeat: ${repeatTiming.verdict}. Conflicting verdicts remain inconclusive.` }; options.onProgress?.(lockIndex + 1, locks.length, lock.id); - return { id: lock.id, title: lock.title, optimumA: lock.expectedActions, solvers, quality, timing, + return { id: lock.id, optimumA: lock.expectedActions, solvers, quality, timing, timingAttempts, calibration: { baselineSelfA: selfA, baselineSelfB: selfB }, - historicalReference: { ...lock.historicalReference, timingCompared: false }, differences: Object.fromEntries(Object.entries(solvers).filter(([name]) => name !== 'candidate').map(([name, other]) => [name, compareMetrics(candidate, other)])) }; }); const aggregates = Object.fromEntries(entries.map(([name]) => [name, results.reduce((total, row) => { @@ -167,7 +152,7 @@ export function runComparison(options: { }, { A: 0, U: 0, C: 0, plateSwitches: 0, sumOfMediansMs: 0 })])); return { schemaVersion: 1, createdAt: new Date().toISOString(), config: { repetitions, warmups, lockCount: locks.length, scope: locks.length === 45 ? 'full-45-catalog' : 'subset', - timingBoundary: 'Only synchronous public solver call; cloning, legacy/reference normalization, replay and metrics excluded.', + timingBoundary: 'Only synchronous public solver call; cloning, legacy normalization, replay and metrics excluded.', order: 'Per-lock baseline-vs-itself calibration first; matched rounds rotate by lock and round. A non-pass receives one bounded repeat with all raw attempts retained.', uncertainty: 'Paired log-ratio mean with conservative 2.776 standard-error envelope; control sets tolerance. >=5 samples; >25% control envelope cannot certify a pass.', gc: 'Natural GC; no forced collection.', fixtureSha256: catalogSha256 }, @@ -189,10 +174,10 @@ export function renderMarkdown(report: ComparisonReport, metadata: unknown, memo lines.push('', '## Isolated process peak RSS', '', 'RSS includes Node, module loading and runtime allocation; it is not exact algorithm heap allocation. Browser peak memory is unavailable. Memory runs are separate from timed calls.', '', '```json', JSON.stringify(memory, null, 2), '```', '', '## Environment and source provenance', '', '```json', JSON.stringify(metadata, null, 2), '```', '', '## Interpretation', '', 'Every warmup and measured solution is replayed independently one unit division at a time, checked against the stored exact action minimum, input immutability and deterministic paths. Target snapshot changes cannot mask candidate drift: the supplied baseline module must reproduce the supplied baseline snapshot.', '', - 'Historical upstream observations have a different objective (unit divisions, then switches); they are never a substitute for live paired timings. U/C/switches are descriptive and are still strict migration regression gates. Timings describe this Node runtime and machine only. A performance inconclusive verdict does not establish absence of regression. Sum of per-lock medians is not a whole-catalog wall-clock median. Raw samples, paths and calibration envelopes are in benchmark.json.', ''); + 'U/C/switches are descriptive and are still strict migration regression gates. Timings describe this Node runtime and machine only. A performance inconclusive verdict does not establish absence of regression. Sum of per-lock medians is not a whole-catalog wall-clock median. Raw samples, paths and calibration envelopes are in benchmark.json.', ''); return lines.join('\n'); } -export async function publishReport(report: ComparisonReport, outputDir: string, metadata: unknown = {}, memory: unknown = {} ) { +export async function publishReport(report: ComparisonReport, outputDir: string, metadata: unknown = {}, memory: unknown = {}, baselineSnapshot?: Snapshot) { if (!report.correctness.passed) throw new Error('Correctness gates must pass before publishing.'); const root = resolve(outputDir); await mkdir(root, { recursive: true }); const staged = await mkdtemp(join(root, '.benchmark-')); @@ -211,10 +196,11 @@ export async function publishReport(report: ComparisonReport, outputDir: string, metadata, memory, config: report.config, aggregates: report.aggregates, reports: { json: { path: 'benchmark.json', sha256: sha256(json) }, markdown: { path: 'benchmark.md', sha256: sha256(markdown) } } }; try { + if (baselineSnapshot !== undefined) await writeSnapshot(join(staged, 'baseline-snapshot.json'), baselineSnapshot); await writeFile(join(staged, 'benchmark.json'), json, { flag: 'wx' }); await writeFile(join(staged, 'benchmark.md'), markdown, { flag: 'wx' }); await writeFile(join(staged, 'benchmark-evidence.json'), `${JSON.stringify(evidence, null, 2)}\n`, { flag: 'wx' }); await rename(staged, directory); } catch (error) { await rm(staged, { recursive: true, force: true }); throw error; } - return { directory, jsonPath: join(directory, 'benchmark.json'), markdownPath: join(directory, 'benchmark.md'), evidencePath: join(directory, 'benchmark-evidence.json') }; + return { directory, ...(baselineSnapshot === undefined ? {} : { baselineSnapshotPath: join(directory, 'baseline-snapshot.json') }), jsonPath: join(directory, 'benchmark.json'), markdownPath: join(directory, 'benchmark.md'), evidencePath: join(directory, 'benchmark-evidence.json') }; } diff --git a/scripts/benchmark-memory-worker.ts b/tests/benchmarks/memory-worker.ts similarity index 60% rename from scripts/benchmark-memory-worker.ts rename to tests/benchmarks/memory-worker.ts index 5e84921..16add05 100644 --- a/scripts/benchmark-memory-worker.ts +++ b/tests/benchmarks/memory-worker.ts @@ -1,22 +1,22 @@ -import { readPeakRss } from '../benchmarks/rss.ts'; -import { loadCatalog } from '../benchmarks/catalog.ts'; -import { loadImplementation, loadUpstream } from '../benchmarks/harness.ts'; -import { commandMetrics, replayCommands } from '../benchmarks/validation.ts'; +import { readPeakRss } from './rss.ts'; +import { loadCatalog } from './catalog.ts'; +import { loadImplementation } from './harness.ts'; +import { commandMetrics, replayCommands } from './validation.ts'; const startupRssBytes = process.memoryUsage().rss; const startupPeak = readPeakRss(); const [modulePath, format, fixtureId] = process.argv.slice(2); -if (modulePath === undefined || (format !== 'tuple' && format !== 'legacy' && format !== 'upstream')) throw new Error('Invalid RSS worker arguments.'); -const solver = format === 'upstream' ? loadUpstream(modulePath) : await loadImplementation(modulePath, format, 'RSS worker'); +if (modulePath === undefined || (format !== 'tuple' && format !== 'legacy')) throw new Error('Invalid RSS worker arguments.'); +const solver = await loadImplementation(modulePath, format, 'RSS worker'); const loadedRssBytes = process.memoryUsage().rss; const loadedPeak = readPeakRss(); const catalog = loadCatalog(); const selectedIds = fixtureId === undefined ? null : new Set(fixtureId.split(',')); const locks = selectedIds === null ? catalog : catalog.filter((lock) => selectedIds.has(lock.id)); if (locks.length === 0) throw new Error('RSS fixture not found.'); for (const lock of locks) { - const commands = solver.normalize(solver.solve(structuredClone(lock.definition))); - if (commands === null || replayCommands(lock.definition, commands).some((position) => position !== 4)) throw new Error('RSS worker solution did not reach target.'); - if (solver.requiresOptimality && commandMetrics(commands).A !== lock.expectedActions) throw new Error('RSS worker action regression.'); + const commands = solver.normalize(solver.solve(structuredClone({ state: lock.state, links: lock.links }))); + if (commands === null || replayCommands(lock, commands).some((position) => position !== 4)) throw new Error('RSS worker solution did not reach target.'); + if (commandMetrics(commands).A !== lock.expectedActions) throw new Error('RSS worker action regression.'); } const finalPeak = readPeakRss(); process.stdout.write(JSON.stringify({ status: 'passed', startupRssBytes, loadedRssBytes, diff --git a/benchmarks/memory.ts b/tests/benchmarks/memory.ts similarity index 98% rename from benchmarks/memory.ts rename to tests/benchmarks/memory.ts index 2f694dc..21b07d5 100644 --- a/benchmarks/memory.ts +++ b/tests/benchmarks/memory.ts @@ -4,14 +4,14 @@ import { record, string } from './validation.ts'; import type { Fixture } from './catalog.ts'; import type { ModuleFormat } from './harness.ts'; import { confirmPerformance, performanceVerdict, statistics } from './comparison.ts'; -export type MemorySpec = { name: string; module: string; format: ModuleFormat | 'upstream' }; +export type MemorySpec = { name: string; module: string; format: ModuleFormat }; export type RssSample = { peakRssBytes: number; startupRssBytes: number; loadedRssBytes: number; lockCount: number; peakRssMetric: string; startupPeakRssBytes: number; loadedPeakRssBytes: number; resourceUsageMaxRssBytes: number; startupResourceUsageMaxRssBytes: number; resourceUsageLaunchFloorDetected: boolean }; function positive(value: unknown): number { if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) throw new Error('Invalid worker RSS measurement.'); return value; } export function measureRss(spec: MemorySpec, fixtureId?: string): RssSample { - const worker = fileURLToPath(new URL('../scripts/benchmark-memory-worker.ts', import.meta.url)); + const worker = fileURLToPath(new URL('./memory-worker.ts', import.meta.url)); const args = [worker, spec.module, spec.format]; if (fixtureId !== undefined) args.push(fixtureId); const stdout = execFileSync(process.execPath, args, { encoding: 'utf8', timeout: 120_000, maxBuffer: 1_048_576, stdio: ['ignore', 'pipe', 'pipe'] }); const parsed: unknown = JSON.parse(stdout); const value = record(parsed, 'RSS worker result'); diff --git a/benchmarks/rss.ts b/tests/benchmarks/rss.ts similarity index 100% rename from benchmarks/rss.ts rename to tests/benchmarks/rss.ts diff --git a/tests/benchmarks/snapshot.ts b/tests/benchmarks/snapshot.ts new file mode 100644 index 0000000..e288f56 --- /dev/null +++ b/tests/benchmarks/snapshot.ts @@ -0,0 +1,46 @@ +import assert from 'node:assert/strict'; +import { mkdir } from 'node:fs/promises'; +import { dirname, resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { assertArtifactOutput, catalogSha256, loadCatalog, type Fixture } from './catalog.ts'; +import { loadImplementation, validateSolution, writeSnapshot, type Implementation, type ModuleFormat, type Snapshot } from './harness.ts'; +import { inspectSource, validateSource } from './source.ts'; + +export function snapshotFromImplementation(solver: Implementation, source: Record, reason: string, locks: readonly Fixture[] = loadCatalog()): Snapshot { + validateSource(source); + if (reason.trim() === '') throw new Error('Snapshot reason is required.'); + return { schemaVersion: 1, fixtureSha256: catalogSha256, source, reason, + locks: locks.map((lock) => { + const input = structuredClone({ state: lock.state, links: lock.links }); + const raw = solver.solve(input); + assert.deepEqual(input, { state: lock.state, links: lock.links }, `${lock.id}: snapshot solver mutated its input.`); + return { id: lock.id, optimumA: lock.expectedActions, ...validateSolution(lock, solver.normalize(raw)) }; + }) }; +} +export async function generateSnapshot(modulePath: string, format: ModuleFormat, reason: string): Promise { + const source = inspectSource(modulePath, format); + const solver = await loadImplementation(modulePath, format, 'Own baseline snapshot source'); + const snapshot = snapshotFromImplementation(solver, source, reason); + inspectSource(modulePath, format, source); + return snapshot; +} +async function main(): Promise { + const fields = new Map(); + const args = process.argv.slice(2); + for (let index = 0; index < args.length; index += 2) { + const key = args[index]; const value = args[index + 1]; + if (key === undefined || value === undefined || !['--source-module', '--source-format', '--reason', '--output'].includes(key) || fields.has(key)) throw new Error('Invalid snapshot arguments.'); + fields.set(key, value); + } + const modulePath = fields.get('--source-module'); const format = fields.get('--source-format'); + const reason = fields.get('--reason'); const output = fields.get('--output'); + if (modulePath === undefined || reason === undefined || output === undefined || (format !== 'tuple' && format !== 'legacy')) throw new Error('Usage: node tests/benchmarks/snapshot.ts --source-module PATH --source-format tuple|legacy --reason TEXT --output NEW_PATH'); + assertArtifactOutput(output); + const snapshot = await generateSnapshot(modulePath, format, reason); + await mkdir(dirname(resolve(output)), { recursive: true }); + await writeSnapshot(output, snapshot); + console.log(JSON.stringify({ snapshotPath: resolve(output), lockCount: snapshot.locks.length, source: snapshot.source })); +} +if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) { + try { await main(); } catch (error) { console.error(error); process.exitCode = 1; } +} diff --git a/tests/benchmarks/source.ts b/tests/benchmarks/source.ts new file mode 100644 index 0000000..f4796f8 --- /dev/null +++ b/tests/benchmarks/source.ts @@ -0,0 +1,105 @@ +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { readFileSync, readdirSync, realpathSync, statSync } from 'node:fs'; +import { basename, dirname, join, relative } from 'node:path'; +import { validateManifest } from '../../.github/scripts/release-lib.ts'; +import { git, jsonFile, sha256 } from './catalog.ts'; +import { record, string } from './validation.ts'; +import type { ModuleFormat } from './harness.ts'; + +export function hashFiles(directory: string, matches: (path: string) => boolean): Record { + const result: Record = {}; + const visit = (path: string): void => { + for (const entry of readdirSync(path, { withFileTypes: true })) { + const full = join(path, entry.name); + if (entry.isDirectory()) visit(full); + else if (entry.isFile() && matches(full)) result[relative(directory, full)] = sha256(readFileSync(full)); + else if (entry.isSymbolicLink()) throw new Error(`Source symlink cannot establish immutable provenance: ${full}`); + } + }; + visit(directory); + return Object.fromEntries(Object.entries(result).sort(([a], [b]) => a.localeCompare(b))); +} +export function revision(directory: string): string | null { try { return git(directory, ['rev-parse', 'HEAD']); } catch { return null; } } +function checkout(module: string): { root: string; revision: string; built: boolean } | null { + const sourceRevision = revision(dirname(module)); + if (sourceRevision === null) return null; + const root = realpathSync(git(dirname(module), ['rev-parse', '--show-toplevel'])); + const tracked = git(root, ['ls-files', '--', relative(root, module)]) !== ''; + const built = !tracked && relative(root, dirname(module)) === 'dist'; + return tracked || built ? { root, revision: sourceRevision, built } : null; +} +export function validateSource(value: unknown): Record { + const source = record(value, 'baseline source identity'); + if (!/^[0-9a-f]{40}$/.test(string(source['revision'], 'baseline source revision')) + || !/^[0-9a-f]{64}$/.test(string(source['moduleSha256'], 'baseline source module hash')) + || (source['format'] !== 'tuple' && source['format'] !== 'legacy')) throw new Error('Baseline source identity is missing or invalid.'); + return source; +} +function verifyReleaseSource(module: string, source: Record): void { + if (source['kind'] !== 'previous-stable-release') throw new Error('A module outside its own checkout requires verified release provenance.'); + const manifestPath = join(dirname(module), 'previous-release-manifest.json'); + const evidencePath = join(dirname(module), 'previous-benchmark-evidence.json'); + const manifestHash = sha256(readFileSync(manifestPath)); const evidenceHash = sha256(readFileSync(evidencePath)); + if (source['releaseManifestSha256'] !== manifestHash || source['releaseEvidenceSha256'] !== evidenceHash) throw new Error('Verified release manifest/evidence hash mismatch.'); + const manifest = validateManifest(jsonFile(manifestPath)); + const snapshot = record(record(jsonFile(evidencePath), 'previous release evidence')['qualitySnapshot'], 'previous release snapshot'); + const original = validateSource(snapshot['source']); + if (manifest.kind !== 'stable' || manifest.version !== source['releaseVersion'] || manifest.sourceSha !== source['revision'] + || manifest.assets['gothic-lock-solver.mjs'] !== source['moduleSha256'] || manifest.assets['benchmark-evidence.json'] !== evidenceHash + || original['revision'] !== source['revision'] || original['moduleSha256'] !== source['moduleSha256'] || original['format'] !== source['format']) { + throw new Error('Verified release module, manifest and snapshot identities disagree.'); + } +} +export function inspectSource(modulePath: string, format: ModuleFormat, pinned?: Record): Record { + const module = realpathSync(modulePath); + if (!statSync(module).isFile()) throw new Error('Baseline module must be a regular file.'); + const moduleSha256 = sha256(readFileSync(module)); + const moduleTree = hashFiles(dirname(module), (path) => /\.(?:mjs|js|ts)$/.test(path)); + if (pinned !== undefined) { + validateSource(pinned); + if (pinned['moduleSha256'] !== moduleSha256 || pinned['format'] !== format) throw new Error('Baseline module hash or format differs from supplied source identity.'); + if (pinned['moduleTree'] !== undefined) assert.deepEqual(moduleTree, pinned['moduleTree'], 'Baseline source tree hash mismatch.'); + } + const location = checkout(module); + let buildManifestSha256: string | undefined; + if (location === null) { + if (pinned === undefined) throw new Error('Baseline requires an own Git checkout or a verified release snapshot with source identity.'); + verifyReleaseSource(module, pinned); + } else { + const { root, revision: sourceRevision } = location; + if (pinned !== undefined && pinned['revision'] !== sourceRevision) throw new Error('Baseline revision differs from supplied source identity.'); + if (location.built) { + const path = join(dirname(module), 'manifest.json'); + const manifest = record(jsonFile(path), 'baseline build manifest'); + if (manifest['schemaVersion'] !== 1 || manifest['package'] !== 'gothic-lock-solver') throw new Error('Unsupported baseline build manifest.'); + if (manifest['sourceSha'] !== sourceRevision) throw new Error('Baseline build manifest revision differs from checkout HEAD.'); + if (record(manifest['files'], 'baseline build checksums')[basename(module)] !== moduleSha256) throw new Error('Baseline build manifest module checksum mismatch.'); + buildManifestSha256 = sha256(readFileSync(path)); + if (pinned?.['buildManifestSha256'] !== undefined && pinned['buildManifestSha256'] !== buildManifestSha256) throw new Error('Baseline build manifest changed after snapshot generation.'); + } + const paths = git(root, ['ls-files', '--', 'src', relative(root, module)]).split('\n').filter((path) => /\.(?:mjs|js|ts)$/.test(path)); + if (paths.length === 0) throw new Error('Baseline checkout has no tracked solver source.'); + const untracked = git(root, ['ls-files', '--others', '--', 'src']).split('\n').filter((path) => /\.(?:mjs|js|ts)$/.test(path)); + if (untracked.length > 0) throw new Error(`Baseline contains untracked source: ${untracked.join(', ')}`); + // The supplied own checkout is the pin: no duplicated implementation or source manifest is needed. + for (const path of paths) { + if (sha256(readFileSync(join(root, path))) !== sha256(execFileSync('git', ['-C', root, 'show', `HEAD:${path}`]))) throw new Error(`Baseline source hash differs from pinned revision: ${path}`); + } + } + return { ...pinned, revision: location?.revision ?? pinned?.['revision'], moduleSha256, format, moduleTree, + ...(buildManifestSha256 === undefined ? {} : { buildManifestSha256 }) }; +} +export function inspectComparisonBaseline(candidatePath: string, baselinePath: string, format: ModuleFormat, pinned?: Record): Record { + const candidate = realpathSync(candidatePath); const baseline = realpathSync(baselinePath); + if (candidate === baseline) throw new Error('Candidate and baseline must not be the same module.'); + const baselineCheckout = checkout(baseline); + const candidateRevision = revision(dirname(candidate)); + if (baselineCheckout !== null && candidateRevision !== null) { + const candidateRoot = realpathSync(git(dirname(candidate), ['rev-parse', '--show-toplevel'])); + if (baselineCheckout.root === candidateRoot) throw new Error('Baseline must not use the candidate checkout.'); + if (baselineCheckout.revision === candidateRevision) throw new Error('Baseline checkout must not use the candidate revision.'); + } + // A release saved inside candidate/artifacts is independent only when its persisted release chain verifies. + return inspectSource(baseline, format, pinned); +} diff --git a/benchmarks/validation.ts b/tests/benchmarks/validation.ts similarity index 83% rename from benchmarks/validation.ts rename to tests/benchmarks/validation.ts index c88cb46..9c2d0fd 100644 --- a/benchmarks/validation.ts +++ b/tests/benchmarks/validation.ts @@ -39,20 +39,6 @@ export function legacyCommands(value: unknown): TupleCommand[] { return [plate - 1, direction === 'left' ? steps : -steps]; }); } -export function groupReferenceClicks(value: unknown): TupleCommand[] | null { - if (value === null) return null; - const commands: [number, number][] = []; - for (const raw of array(value, 'reference result')) { - const click = record(raw, 'reference click'); - const index = integer(click['plate'], 'reference plate'); - const delta = integer(click['d'], 'reference delta'); - if (index < 0 || Math.abs(delta) !== 1) throw new Error('Invalid reference unit click.'); - const last = commands.at(-1); - if (last !== undefined && last[0] === index && Math.sign(last[1]) === delta) last[1] += delta; - else commands.push([index, delta]); - } - return tupleCommands(commands); -} export function replayCommands(definition: Definition, value: unknown): number[] { let state = [...definition.state]; for (const [index, delta] of tupleCommands(value)) { diff --git a/tests/distribution/fixtures/consumer.mjs b/tests/distribution/fixtures/consumer.mjs deleted file mode 100644 index e50d0b4..0000000 --- a/tests/distribution/fixtures/consumer.mjs +++ /dev/null @@ -1,44 +0,0 @@ -import assert from 'node:assert/strict'; -import { readFileSync, writeFileSync, existsSync } from 'node:fs'; -import { spawnSync } from 'node:child_process'; -import { delimiter, dirname } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { solveLock } from 'gothic-lock-solver'; -import * as GothicLockSolver from 'gothic-lock-solver'; - -assert.deepEqual(solveLock([6, 2], [[0, -1], [0, 0]]), [[0, -2]]); -assert.deepEqual(GothicLockSolver.solveLock([4, 4], [[0, 0], [0, 0]]), []); -assert.deepEqual(Object.keys(GothicLockSolver).sort(), ['LockInputError', 'SearchLimitError', 'solveLock']); -assert.ok(import.meta.resolve('gothic-lock-solver').includes('/node_modules/gothic-lock-solver/dist/')); -const manifest = JSON.parse(readFileSync(new URL('./node_modules/gothic-lock-solver/package.json', import.meta.url), 'utf8')); -assert.deepEqual(manifest.dependencies ?? {}, {}); -assert.deepEqual(manifest.optionalDependencies ?? {}, {}); -assert.deepEqual(manifest.peerDependencies ?? {}, {}); -assert.deepEqual(manifest.bundleDependencies ?? [], []); -assert.equal(manifest.private, false); -for (const lifecycle of ['preinstall', 'install', 'postinstall', 'prepare']) assert.equal(manifest.scripts?.[lifecycle], undefined); -assert.deepEqual(manifest.bin, { 'gothic-lock-solver': './dist/gothic-lock-solver.cli.mjs' }); -for (const path of ['gothic-lock-solver/dist/gothic-lock-solver.mjs', 'gothic-lock-solver/dist/gothic-lock-solver.min.mjs']) { - const api = await import(path); - assert.deepEqual(Object.keys(api).sort(), Object.keys(GothicLockSolver).sort()); - assert.deepEqual(api.solveLock([6, 2], [[0, -1], [0, 0]]), [[0, -2]]); -} -console.log(JSON.stringify({ node: process.version, installed: true, namespace: Object.keys(GothicLockSolver).sort() })); - -const input = new URL('./lock.json', import.meta.url); -const output = new URL('./solution.json', import.meta.url); -const executable = new URL('./node_modules/gothic-lock-solver/dist/gothic-lock-solver.cli.mjs', import.meta.url); -const bin = fileURLToPath(new URL('./node_modules/.bin/gothic-lock-solver', import.meta.url)); -assert.ok(existsSync(bin)); -writeFileSync(input, JSON.stringify({ state: [6, 2], links: [[0, -1], [0, 0]] })); -let cli = spawnSync(bin, [fileURLToPath(input), '--output', fileURLToPath(output)], { encoding: 'utf8', env: { ...process.env, PATH: `${dirname(process.execPath)}${delimiter}${process.env.PATH ?? ''}` } }); -assert.equal(cli.status, 0); -assert.equal(cli.stderr, ''); -assert.deepEqual(JSON.parse(readFileSync(output, 'utf8')).commands, [{ plate: 1, direction: 'right', steps: 2 }]); -writeFileSync(input, JSON.stringify({ state: [1, 7], links: [[0, 1], [1, 0]] })); -cli = spawnSync(process.execPath, [fileURLToPath(executable), fileURLToPath(input)], { encoding: 'utf8' }); -assert.equal(cli.status, 2); -writeFileSync(input, '{'); -cli = spawnSync(process.execPath, [fileURLToPath(executable), fileURLToPath(input)], { encoding: 'utf8' }); -assert.equal(cli.status, 1); -console.log(JSON.stringify({ installedCli: true, node: process.version, exitCodes: [0, 1, 2] })); diff --git a/tests/distribution/fixtures/global-consumer.ts b/tests/distribution/fixtures/global-consumer.ts deleted file mode 100644 index 7c1ed7d..0000000 --- a/tests/distribution/fixtures/global-consumer.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -const globalCommands: readonly GothicLockSolver.Command[] | null = GothicLockSolver.solveLock([6, 2], [[0, -1], [0, 0]]); -void globalCommands; -// @ts-expect-error The classic namespace exposes no default property. -GothicLockSolver.default; diff --git a/tests/types/assets.d.ts b/tests/e2e/assets.d.ts similarity index 100% rename from tests/types/assets.d.ts rename to tests/e2e/assets.d.ts diff --git a/tests/browser/catalog.test.ts b/tests/e2e/browser/catalog.test.ts similarity index 71% rename from tests/browser/catalog.test.ts rename to tests/e2e/browser/catalog.test.ts index ed00a87..763c615 100644 --- a/tests/browser/catalog.test.ts +++ b/tests/e2e/browser/catalog.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from 'vitest'; -import rawCatalog from '../../benchmarks/fixtures/catalog.json' with { type: 'json' }; +import rawCatalog from '../../benchmarks/fixtures.json' with { type: 'json' }; import { replayCommands } from '../../benchmarks/validation.ts'; -import { solveLock, type Link, type Position } from '../../src/index.ts'; +import { solveLock, type Link, type Position } from '../../../src/index.ts'; function position(value: number): Position { if (value === 1 || value === 2 || value === 3 || value === 4 || value === 5 || value === 6 || value === 7) return value; @@ -13,19 +13,19 @@ function link(value: number): Link { } describe('shared immutable 45-lock catalog', () => { - test('contains exactly 45 unique original fixtures', () => { + test('contains exactly 45 unique fixed fixtures', () => { expect(rawCatalog).toHaveLength(45); expect(new Set(rawCatalog.map(({ id }) => id)).size).toBe(45); }); for (const fixture of rawCatalog) { test(`${fixture.id}: independently replays the exact action minimum`, () => { - const state = fixture.definition.state.map(position); - const links = fixture.definition.links.map((row) => row.map(link)); + const state = fixture.state.map(position); + const links = fixture.links.map((row) => row.map(link)); const before = JSON.stringify([state, links]); const commands = solveLock(state, links); expect(commands).not.toBeNull(); expect(commands).toHaveLength(fixture.expectedActions); - expect(replayCommands(fixture.definition, commands)).toEqual(state.map(() => 4)); + expect(replayCommands(fixture, commands)).toEqual(state.map(() => 4)); expect(JSON.stringify([state, links])).toBe(before); expect(solveLock(state, links)).toEqual(commands); }); diff --git a/tests/e2e/build-fixtures.ts b/tests/e2e/build-fixtures.ts new file mode 100644 index 0000000..2be61b6 --- /dev/null +++ b/tests/e2e/build-fixtures.ts @@ -0,0 +1,40 @@ +import { rm } from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; +import { build } from 'vite'; + +const root = fileURLToPath(new URL('../../', import.meta.url)); +const output = fileURLToPath(new URL('../../artifacts/e2e/fixtures/', import.meta.url)); +await rm(output, { recursive: true, force: true }); +for (const minified of [false, true]) { + const suffix = minified ? '.min' : ''; + for (const entry of ['module', 'worker', 'classic']) { + await build({ + root, + configFile: false, + logLevel: 'warn', + plugins: [{ + name: 'built-library-consumer', + enforce: 'pre', + resolveId(source) { + if (source === '../../../src/index.ts') { + return { id: `../../../dist/gothic-lock-solver${suffix}.mjs`, external: true }; + } + return null; + }, + }], + build: { + target: 'es2022', + outDir: output, + emptyOutDir: false, + minify: false, + lib: { + name: 'GothicConsumer', + entry: fileURLToPath(new URL(`./fixtures/${entry}.ts`, import.meta.url)), + formats: [entry === 'classic' ? 'iife' : 'es'], + fileName: () => `${entry}${suffix}.${entry === 'classic' ? 'js' : 'mjs'}`, + }, + rolldownOptions: { output: { codeSplitting: false } }, + }, + }); + } +} diff --git a/tests/e2e/catalog.ts b/tests/e2e/catalog.ts index faf4cc5..212f68f 100644 --- a/tests/e2e/catalog.ts +++ b/tests/e2e/catalog.ts @@ -2,8 +2,8 @@ import assert from 'node:assert/strict'; import { execFileSync } from 'node:child_process'; import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; -import { loadCatalog as loadPinnedCatalog } from '../../benchmarks/catalog.ts'; -import { array, replayCommands, tupleCommands } from '../../benchmarks/validation.ts'; +import { loadCatalog as loadPinnedCatalog } from '../benchmarks/catalog.ts'; +import { array, replayCommands, tupleCommands } from '../benchmarks/validation.ts'; import { distDirectory } from './paths.ts'; export interface CatalogFixture { @@ -14,10 +14,10 @@ export interface CatalogFixture { } export function loadCatalog(): readonly CatalogFixture[] { - return loadPinnedCatalog().map(({ id, definition, expectedActions }) => ({ + return loadPinnedCatalog().map(({ id, state, links, expectedActions }) => ({ id, - state: definition.state, - links: definition.links, + state, + links, expectedActions, })); } diff --git a/tests/e2e/consumers.spec.ts b/tests/e2e/consumers.spec.ts index abd45a7..39b958c 100644 --- a/tests/e2e/consumers.spec.ts +++ b/tests/e2e/consumers.spec.ts @@ -47,8 +47,11 @@ function exerciseConsumer(fixtures: readonly CatalogFixture[]) { if (typeof solveLock !== 'function') { throw new Error('Missing public solveLock function'); } + const factory: unknown = Reflect.get(consumer, 'createSolverConfig'); + if (typeof factory !== 'function') throw new Error('Missing configuration factory'); + const config: unknown = Reflect.apply(factory, undefined, [{ maxDenseBytes: 0 }]); const invoke = (state: unknown, links: unknown): unknown => Reflect.apply( - solveLock, undefined, [state, links], + solveLock, undefined, [state, links, config], ); const publicExports: unknown = Reflect.get(consumer, 'exports'); const namedMatchesNamespace: unknown = Reflect.get(consumer, 'namedMatchesNamespace'); @@ -61,6 +64,8 @@ function exerciseConsumer(fixtures: readonly CatalogFixture[]) { } return { publicExports, + config, + configFrozen: Object.isFrozen(config), namedMatchesNamespace, hasClassicGlobal, signed: invoke([6, 2], [[0, -1], [0, 0]]), @@ -92,6 +97,9 @@ for (const format of formats) { const result = await page.evaluate(exerciseConsumer, catalog); expect(result.publicExports).toContain('solveLock'); + expect(result.publicExports).toContain('createSolverConfig'); + expect(result.config).toEqual({ maxVisited: 2_000_000, maxExpanded: 1_000_000, maxFrontier: 1_000_000, maxDenseBytes: 0 }); + expect(result.configFrozen).toBe(true); expect(result.publicExports).not.toContain('default'); expect(result.namedMatchesNamespace).toBe(true); expect(result.hasClassicGlobal).toBe(format.classic); @@ -115,6 +123,7 @@ for (const format of formats) { expect(observed.requests.sort()).toEqual([ `http://127.0.0.1:4177${fixturePath}`, `http://127.0.0.1:4177${bundlePath}`, + `http://127.0.0.1:4177/tests/e2e/fixtures/${format.fixture.replace('.html', format.classic ? '.js' : '.mjs')}`, ].sort()); }); } @@ -167,12 +176,15 @@ for (const minified of [false, true]) { const html = await readFile(new URL(`./fixtures/${fixtureName}`, import.meta.url), 'utf8'); await writeFile(join(isolatedDirectory, fixtureName), html.replace('src="../../../dist/', 'src="./')); await copyFile(join(distDirectory, bundleName), join(isolatedDirectory, bundleName)); + const consumerName = `classic${suffix}.js`; + await copyFile(new URL(`../../artifacts/e2e/fixtures/${consumerName}`, import.meta.url), join(isolatedDirectory, consumerName)); + const consumerUrl = pathToFileURL(join(isolatedDirectory, consumerName)).href; const fixtureUrl = pathToFileURL(join(isolatedDirectory, fixtureName)).href; const bundleUrl = pathToFileURL(join(isolatedDirectory, bundleName)).href; await page.goto(fixtureUrl); await expect(page.locator('#status')).toHaveText('ready'); expect(page.url()).toBe(fixtureUrl); - const externalScript = page.locator('script[src]'); + const externalScript = page.locator('script[src]').first(); await expect(externalScript).toHaveCount(1); await expect(externalScript).toHaveJSProperty('src', bundleUrl); await expect(externalScript).toHaveJSProperty('type', ''); @@ -186,7 +198,7 @@ for (const minified of [false, true]) { expect(observed.failures).toEqual([]); // Firefox does not emit request events for file:// loads. Verify the actual // local document/script above, and reject unexpected URLs when events exist. - const allowedRequests = new Set([fixtureUrl, bundleUrl]); + const allowedRequests = new Set([fixtureUrl, bundleUrl, consumerUrl]); expect(observed.requests.filter((url) => !allowedRequests.has(url))).toEqual([]); } finally { await rm(isolatedDirectory, { recursive: true, force: true }); diff --git a/tests/distribution/artifacts.test.ts b/tests/e2e/distribution/artifacts.test.ts similarity index 86% rename from tests/distribution/artifacts.test.ts rename to tests/e2e/distribution/artifacts.test.ts index 014b439..3c2b437 100644 --- a/tests/distribution/artifacts.test.ts +++ b/tests/e2e/distribution/artifacts.test.ts @@ -4,7 +4,7 @@ import { readFile, readdir } from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; import { describe, expect, test } from 'vitest'; -const dist = new URL('../../dist/', import.meta.url); +const dist = new URL('../../../dist/', import.meta.url); const runtime = ['gothic-lock-solver.js', 'gothic-lock-solver.min.js', 'gothic-lock-solver.min.mjs', 'gothic-lock-solver.mjs']; describe('published artifacts', () => { @@ -39,14 +39,14 @@ describe('published artifacts', () => { expect(api).not.toHaveProperty('default'); expect(api).toHaveProperty('solveLock'); const node = await import('node:child_process'); - const output = node.execFileSync(process.execPath, ['--input-type=module', '-e', `import { solveLock } from ${JSON.stringify(url)}; import * as api from ${JSON.stringify(url)}; console.log(JSON.stringify([solveLock([6,2],[[0,-1],[0,0]]),api.solveLock([4,4],[[0,0],[0,0]]),Object.keys(api).sort()]));`], { encoding: 'utf8', cwd: fileURLToPath(dist) }); - expect(JSON.parse(output)).toEqual([[[0, -2]], [], ['LockInputError', 'SearchLimitError', 'solveLock']]); + const output = node.execFileSync(process.execPath, ['--input-type=module', '-e', `import { solveLock, createSolverConfig } from ${JSON.stringify(url)}; import * as api from ${JSON.stringify(url)}; console.log(JSON.stringify([solveLock([6,2],[[0,-1],[0,0]],createSolverConfig({maxDenseBytes:0})),api.solveLock([4,4],[[0,0],[0,0]]),Object.keys(api).sort()]));`], { encoding: 'utf8', cwd: fileURLToPath(dist) }); + expect(JSON.parse(output)).toEqual([[[0, -2]], [], ['LockInputError', 'SearchLimitError', 'createSolverConfig', 'solveLock']]); }); } test('manifest binds every runtime file and declaration to its bytes', async () => { const raw: unknown = JSON.parse(await readFile(new URL('manifest.json', dist), 'utf8')); - const packageMetadata: unknown = JSON.parse(await readFile(new URL('../../package.json', import.meta.url), 'utf8')); + const packageMetadata: unknown = JSON.parse(await readFile(new URL('../../../package.json', import.meta.url), 'utf8')); if (typeof packageMetadata !== 'object' || packageMetadata === null || !('version' in packageMetadata)) throw new Error('Malformed package metadata'); expect(raw).toMatchObject({ schemaVersion: 1, package: 'gothic-lock-solver', version: packageMetadata.version, sourceSha: execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim() }); if (typeof raw !== 'object' || raw === null || !('files' in raw) || typeof raw.files !== 'object' || raw.files === null) throw new Error('Malformed distribution manifest'); diff --git a/tests/distribution/catalog.test.ts b/tests/e2e/distribution/catalog.test.ts similarity index 74% rename from tests/distribution/catalog.test.ts rename to tests/e2e/distribution/catalog.test.ts index 1d706a0..396ce20 100644 --- a/tests/distribution/catalog.test.ts +++ b/tests/e2e/distribution/catalog.test.ts @@ -6,7 +6,7 @@ import { replayCommands } from '../../benchmarks/validation.ts'; for (const filename of ['gothic-lock-solver.mjs', 'gothic-lock-solver.min.mjs']) { test(`${filename}: real Node loader solves all 45 fixtures at the exact minimum`, () => { const catalog = loadCatalog(); - const code = `import { solveLock } from ${JSON.stringify(new URL(`../../dist/${filename}`, import.meta.url).href)};let input='';for await (const chunk of process.stdin) input+=chunk;console.log(JSON.stringify(JSON.parse(input).map(({definition:{state,links}})=>solveLock(state,links))));`; + const code = `import { solveLock } from ${JSON.stringify(new URL(`../../../dist/${filename}`, import.meta.url).href)};let input='';for await (const chunk of process.stdin) input+=chunk;console.log(JSON.stringify(JSON.parse(input).map(({state,links})=>solveLock(state,links))));`; const output = execFileSync(process.execPath, ['--input-type=module', '-e', code], { input: JSON.stringify(catalog), encoding: 'utf8' }); const results: unknown = JSON.parse(output); if (!Array.isArray(results)) throw new Error('Node consumer returned a malformed result'); @@ -14,7 +14,7 @@ for (const filename of ['gothic-lock-solver.mjs', 'gothic-lock-solver.min.mjs']) for (const [index, fixture] of catalog.entries()) { const commands: unknown = results[index]; expect(commands).toHaveLength(fixture.expectedActions); - expect(replayCommands(fixture.definition, commands)).toEqual(fixture.definition.state.map(() => 4)); + expect(replayCommands(fixture, commands)).toEqual(fixture.state.map(() => 4)); } }); } diff --git a/tests/distribution/cli.test.ts b/tests/e2e/distribution/cli.test.ts similarity index 78% rename from tests/distribution/cli.test.ts rename to tests/e2e/distribution/cli.test.ts index c317546..d9d8d05 100644 --- a/tests/distribution/cli.test.ts +++ b/tests/e2e/distribution/cli.test.ts @@ -11,14 +11,16 @@ describe('standalone Node CLI artifact', () => { const executable = join(directory, 'gothic-lock-solver.cli.mjs'); const input = join(directory, 'lock.json'); const output = join(directory, 'solution.json'); - await copyFile(new URL('../../dist/gothic-lock-solver.cli.mjs', import.meta.url), executable); + const config = join(directory, 'config.json'); + await writeFile(config, JSON.stringify({ maxDenseBytes: 0 })); + await copyFile(new URL('../../../dist/gothic-lock-solver.cli.mjs', import.meta.url), executable); const contents = await readFile(executable, 'utf8'); expect(contents.startsWith('#!/usr/bin/env node\n')).toBe(true); // The sole Node file can import builtins; no sibling core modules or npm // runtime package is present in this temporary consumer directory. expect(contents).not.toMatch(/\bfrom\s*["'](?:\.|\/|gothic-lock-solver)/); await writeFile(input, JSON.stringify({ state: [6, 2], links: [[0, -1], [0, 0]] })); - const solved = spawnSync(process.execPath, [executable, input, '--output', output], { cwd: directory, encoding: 'utf8' }); + const solved = spawnSync(process.execPath, [executable, input, '--config', config, '--output', output], { cwd: directory, encoding: 'utf8' }); expect(solved.status).toBe(0); expect(solved.stderr).toBe(''); expect(solved.stdout).toContain('Найдено команд: 1'); @@ -27,6 +29,10 @@ describe('standalone Node CLI artifact', () => { const unreachable = spawnSync(process.execPath, [executable, input], { cwd: directory, encoding: 'utf8' }); expect(unreachable.status).toBe(2); expect(unreachable.stdout).toContain('Решение не найдено.'); + await writeFile(config, JSON.stringify({ unknown: 1 })); + const invalidConfig = spawnSync(process.execPath, [executable, input, '--config', config], { cwd: directory, encoding: 'utf8' }); + expect(invalidConfig.status).toBe(1); + expect(invalidConfig.stderr).not.toBe(''); await writeFile(input, '{'); const invalid = spawnSync(process.execPath, [executable, input], { cwd: directory, encoding: 'utf8' }); expect(invalid.status).toBe(1); diff --git a/tests/e2e/distribution/fixtures/global-consumer.ts b/tests/e2e/distribution/fixtures/global-consumer.ts new file mode 100644 index 0000000..27d919a --- /dev/null +++ b/tests/e2e/distribution/fixtures/global-consumer.ts @@ -0,0 +1,8 @@ +/// +const globalConfig: GothicLockSolver.SolverConfig = GothicLockSolver.createSolverConfig({ maxDenseBytes: 0 }); +const globalCommands: readonly GothicLockSolver.Command[] | null = GothicLockSolver.solveLock([6, 2], [[0, -1], [0, 0]], globalConfig); +void globalCommands; +// @ts-expect-error The classic namespace exposes no default property. +GothicLockSolver.default; +// @ts-expect-error The global solver requires a complete configuration. +GothicLockSolver.solveLock([4, 4], [[0, 0], [0, 0]], { maxExpanded: 0 }); diff --git a/tests/e2e/distribution/fixtures/runtime-consumer.ts b/tests/e2e/distribution/fixtures/runtime-consumer.ts new file mode 100644 index 0000000..0c3dc0d --- /dev/null +++ b/tests/e2e/distribution/fixtures/runtime-consumer.ts @@ -0,0 +1,62 @@ +import assert from 'node:assert/strict'; +import { readFileSync, writeFileSync, existsSync } from 'node:fs'; +import { spawnSync } from 'node:child_process'; +import { delimiter, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { solveLock, createSolverConfig } from 'gothic-lock-solver'; +import * as GothicLockSolver from 'gothic-lock-solver'; + +const config = createSolverConfig({ maxDenseBytes: 0 }); +assert.ok(Object.isFrozen(config)); +assert.deepEqual(solveLock([6, 2], [[0, -1], [0, 0]], config), [[0, -2]]); +assert.deepEqual(GothicLockSolver.solveLock([4, 4], [[0, 0], [0, 0]]), []); +assert.deepEqual(Object.keys(GothicLockSolver).sort(), ['LockInputError', 'SearchLimitError', 'createSolverConfig', 'solveLock']); +assert.ok(import.meta.resolve('gothic-lock-solver').includes('/node_modules/gothic-lock-solver/dist/')); +const manifest: unknown = JSON.parse(readFileSync(new URL('./node_modules/gothic-lock-solver/package.json', import.meta.url), 'utf8')); +assert.ok(typeof manifest === 'object' && manifest !== null); +for (const field of ['dependencies', 'optionalDependencies', 'peerDependencies']) assert.deepEqual(Reflect.get(manifest, field) ?? {}, {}); +assert.deepEqual(Reflect.get(manifest, 'bundleDependencies') ?? [], []); +assert.equal(Reflect.get(manifest, 'private'), false); +const scripts: unknown = Reflect.get(manifest, 'scripts'); +assert.ok(typeof scripts === 'object' && scripts !== null); +for (const lifecycle of ['preinstall', 'install', 'postinstall', 'prepare']) assert.equal(Reflect.get(scripts, lifecycle), undefined); +assert.deepEqual(Reflect.get(manifest, 'bin'), { 'gothic-lock-solver': './dist/gothic-lock-solver.cli.mjs' }); +for (const path of ['gothic-lock-solver/dist/gothic-lock-solver.mjs', 'gothic-lock-solver/dist/gothic-lock-solver.min.mjs']) { + const api: unknown = await import(path); + assert.ok(typeof api === 'object' && api !== null); + assert.deepEqual(Object.keys(api).sort(), Object.keys(GothicLockSolver).sort()); + const factory: unknown = Reflect.get(api, 'createSolverConfig'); + const solve: unknown = Reflect.get(api, 'solveLock'); + assert.equal(typeof factory, 'function'); + assert.equal(typeof solve, 'function'); + if (typeof factory !== 'function' || typeof solve !== 'function') throw new Error('Malformed ESM exports'); + const settings: unknown = Reflect.apply(factory, undefined, [{ maxDenseBytes: 0 }]); + assert.deepEqual(settings, config); + assert.deepEqual(Reflect.apply(solve, undefined, [[6, 2], [[0, -1], [0, 0]], settings]), [[0, -2]]); +} +console.log(JSON.stringify({ node: process.version, installed: true, namespace: Object.keys(GothicLockSolver).sort() })); + +const input = new URL('./lock.json', import.meta.url); +const configFile = new URL('./config.json', import.meta.url); +const output = new URL('./solution.json', import.meta.url); +const executable = new URL('./node_modules/gothic-lock-solver/dist/gothic-lock-solver.cli.mjs', import.meta.url); +const bin = fileURLToPath(new URL('./node_modules/.bin/gothic-lock-solver', import.meta.url)); +assert.ok(existsSync(bin)); +writeFileSync(input, JSON.stringify({ state: [6, 2], links: [[0, -1], [0, 0]] })); +writeFileSync(configFile, JSON.stringify({ maxDenseBytes: 0 })); +let cli = spawnSync(bin, [fileURLToPath(input), '--config', fileURLToPath(configFile), '--output', fileURLToPath(output)], { encoding: 'utf8', env: { ...process.env, PATH: `${dirname(process.execPath)}${delimiter}${process.env['PATH'] ?? ''}` } }); +assert.equal(cli.status, 0); +assert.equal(cli.stderr, ''); +const solution: unknown = JSON.parse(readFileSync(output, 'utf8')); +assert.ok(typeof solution === 'object' && solution !== null); +assert.deepEqual(Reflect.get(solution, 'commands'), [{ plate: 1, direction: 'right', steps: 2 }]); +writeFileSync(input, JSON.stringify({ state: [1, 7], links: [[0, 1], [1, 0]] })); +cli = spawnSync(process.execPath, [fileURLToPath(executable), fileURLToPath(input)], { encoding: 'utf8' }); +assert.equal(cli.status, 2); +writeFileSync(configFile, JSON.stringify({ unknown: 1 })); +cli = spawnSync(process.execPath, [fileURLToPath(executable), fileURLToPath(input), '--config', fileURLToPath(configFile)], { encoding: 'utf8' }); +assert.equal(cli.status, 1); +writeFileSync(input, '{'); +cli = spawnSync(process.execPath, [fileURLToPath(executable), fileURLToPath(input)], { encoding: 'utf8' }); +assert.equal(cli.status, 1); +console.log(JSON.stringify({ installedCli: true, node: process.version, exitCodes: [0, 1, 2] })); diff --git a/tests/distribution/fixtures/consumer.mts b/tests/e2e/distribution/fixtures/types-consumer.ts similarity index 64% rename from tests/distribution/fixtures/consumer.mts rename to tests/e2e/distribution/fixtures/types-consumer.ts index 85d32d8..3a28242 100644 --- a/tests/distribution/fixtures/consumer.mts +++ b/tests/e2e/distribution/fixtures/types-consumer.ts @@ -1,15 +1,17 @@ -import { solveLock, LockInputError, SearchLimitError } from 'gothic-lock-solver'; +import { solveLock, createSolverConfig, LockInputError, SearchLimitError } from 'gothic-lock-solver'; import * as GothicLockSolver from 'gothic-lock-solver'; import { solveLock as minified } from 'gothic-lock-solver/dist/gothic-lock-solver.min.mjs'; import { solveLock as readable } from 'gothic-lock-solver/dist/gothic-lock-solver.mjs'; -import type { Command, Delta, Link, Links, Position, State, SearchLimit } from 'gothic-lock-solver'; +import type { Command, Delta, Link, Links, Position, State, SearchLimit, SolverConfig } from 'gothic-lock-solver'; const state: State = [6, 2]; const position: Position = 4; const delta: Delta = -2; const link: Link = -1; const links: Links = [[0, link], [0, 0]]; -const commands: readonly Command[] | null = solveLock(state, links); +const config: SolverConfig = createSolverConfig({ maxExpanded: 100, maxDenseBytes: 0 }); +const commands: readonly Command[] | null = solveLock(state, links, config); +solveLock(state, links, createSolverConfig()); const dynamic: Position[] = [position, position]; const dynamicLinks: Link[][] = [[0, 0], [0, 0]]; const limit: SearchLimit | undefined = undefined; @@ -21,7 +23,7 @@ readable([4, 4], [[0, 0], [0, 0]]); import defaultSolver from 'gothic-lock-solver'; void defaultSolver; // @ts-expect-error Internal search modules are not public package exports. -import internalSearch from 'gothic-lock-solver/dist/matrix-search.js'; +import internalSearch from 'gothic-lock-solver/dist/astar.js'; void internalSearch; // @ts-expect-error Position zero is outside the lock model. solveLock([0, 4], [[0, 0], [0, 0]]); @@ -29,8 +31,16 @@ solveLock([0, 4], [[0, 0], [0, 0]]); solveLock([4], [[0]]); // @ts-expect-error Fixed input tuples require a matching matrix. solveLock([4, 4], [[0, 0]]); -// @ts-expect-error There is no third public configuration argument. +// @ts-expect-error The solver requires a complete configuration object. solveLock([4, 4], [[0, 0], [0, 0]], {}); // @ts-expect-error A returned zero delta would be invalid. const zero: Command = [0, 0]; void zero; +// @ts-expect-error Solver configuration is readonly. +config.maxExpanded = 1; +// @ts-expect-error Configuration fields require numbers. +createSolverConfig({ maxVisited: '100' }); +// @ts-expect-error Unknown factory options are invalid. +createSolverConfig({ timeout: 100 }); +// @ts-expect-error Configured fixed tuples still require matching matrix dimensions. +solveLock([4, 4], [[0]], config); diff --git a/tests/e2e/fixtures/classic.html b/tests/e2e/fixtures/classic.html index 6f3f64a..f3ee919 100644 --- a/tests/e2e/fixtures/classic.html +++ b/tests/e2e/fixtures/classic.html @@ -5,13 +5,5 @@ Plain HTML classic consumer loading - + diff --git a/tests/e2e/fixtures/classic.min.html b/tests/e2e/fixtures/classic.min.html index db22e5e..d741389 100644 --- a/tests/e2e/fixtures/classic.min.html +++ b/tests/e2e/fixtures/classic.min.html @@ -5,13 +5,5 @@ Plain HTML minified classic consumer loading - + diff --git a/tests/e2e/fixtures/classic.ts b/tests/e2e/fixtures/classic.ts new file mode 100644 index 0000000..7b739e3 --- /dev/null +++ b/tests/e2e/fixtures/classic.ts @@ -0,0 +1,12 @@ +const namespace: unknown = Reflect.get(globalThis, 'GothicLockSolver'); +if (typeof namespace !== 'object' || namespace === null) throw new Error('Missing classic global'); +Reflect.set(globalThis, 'consumer', { + solveLock: Reflect.get(namespace, 'solveLock'), + createSolverConfig: Reflect.get(namespace, 'createSolverConfig'), + exports: Object.keys(namespace), + namedMatchesNamespace: true, + hasClassicGlobal: Object.hasOwn(globalThis, 'GothicLockSolver'), +}); +const status = document.querySelector('#status'); +if (status === null) throw new Error('Missing consumer status'); +status.textContent = 'ready'; diff --git a/examples/lock.example.json b/tests/e2e/fixtures/lock.example.json similarity index 100% rename from examples/lock.example.json rename to tests/e2e/fixtures/lock.example.json diff --git a/tests/e2e/fixtures/module.html b/tests/e2e/fixtures/module.html index 9780752..f32b198 100644 --- a/tests/e2e/fixtures/module.html +++ b/tests/e2e/fixtures/module.html @@ -4,15 +4,5 @@ Plain HTML ESM consumer loading - + diff --git a/tests/e2e/fixtures/module.min.html b/tests/e2e/fixtures/module.min.html index 4cb6da1..5c640df 100644 --- a/tests/e2e/fixtures/module.min.html +++ b/tests/e2e/fixtures/module.min.html @@ -4,15 +4,5 @@ Plain HTML minified ESM consumer loading - + diff --git a/tests/e2e/fixtures/module.ts b/tests/e2e/fixtures/module.ts new file mode 100644 index 0000000..e92359b --- /dev/null +++ b/tests/e2e/fixtures/module.ts @@ -0,0 +1,13 @@ +import { createSolverConfig, solveLock } from '../../../src/index.ts'; +import * as namespace from '../../../src/index.ts'; + +Reflect.set(globalThis, 'consumer', { + solveLock, + createSolverConfig, + exports: Object.keys(namespace), + namedMatchesNamespace: solveLock === namespace.solveLock && createSolverConfig === namespace.createSolverConfig, + hasClassicGlobal: Object.hasOwn(globalThis, 'GothicLockSolver'), +}); +const status = document.querySelector('#status'); +if (status === null) throw new Error('Missing consumer status'); +status.textContent = 'ready'; diff --git a/tests/e2e/fixtures/worker.min.mjs b/tests/e2e/fixtures/worker.min.mjs deleted file mode 100644 index b70b6d2..0000000 --- a/tests/e2e/fixtures/worker.min.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import { solveLock } from '../../../dist/gothic-lock-solver.min.mjs'; - -self.addEventListener('message', ({ data }) => { - self.postMessage(solveLock(data.state, data.links)); -}); diff --git a/tests/e2e/fixtures/worker.mjs b/tests/e2e/fixtures/worker.mjs deleted file mode 100644 index 42368dc..0000000 --- a/tests/e2e/fixtures/worker.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import { solveLock } from '../../../dist/gothic-lock-solver.mjs'; - -self.addEventListener('message', ({ data }) => { - self.postMessage(solveLock(data.state, data.links)); -}); diff --git a/tests/e2e/fixtures/worker.ts b/tests/e2e/fixtures/worker.ts new file mode 100644 index 0000000..69d0045 --- /dev/null +++ b/tests/e2e/fixtures/worker.ts @@ -0,0 +1,9 @@ +import { createSolverConfig, solveLock } from '../../../src/index.ts'; + +self.addEventListener('message', (event: MessageEvent) => { + const data = event.data; + if (typeof data !== 'object' || data === null || !('state' in data) || !('links' in data)) { + throw new Error('Malformed Worker input'); + } + self.postMessage(Reflect.apply(solveLock, undefined, [data.state, data.links, createSolverConfig()])); +}); diff --git a/tests/e2e/server.ts b/tests/e2e/server.ts index 467f133..1dc688a 100644 --- a/tests/e2e/server.ts +++ b/tests/e2e/server.ts @@ -16,12 +16,14 @@ const fixtureFiles = [ 'classic.html', 'classic.min.html', 'worker.html', - 'worker.mjs', - 'worker.min.mjs', + ]; +const generatedFiles = ['module.mjs', 'module.min.mjs', 'worker.mjs', 'worker.min.mjs', 'classic.js', 'classic.min.js']; +const generated = new URL('../../artifacts/e2e/fixtures/', import.meta.url); const paths = new Set([ ...runtimeFiles.map((file) => `/dist/${file}`), ...fixtureFiles.map((file) => `/tests/e2e/fixtures/${file}`), + ...generatedFiles.map((file) => `/tests/e2e/fixtures/${file}`), ]); // Starting this server never builds or transforms the artifacts being tested. @@ -52,7 +54,9 @@ const server = createServer((request, response) => { } const file = pathname.startsWith('/dist/') ? join(distDirectory, pathname.slice('/dist/'.length)) - : new URL(pathname.slice(1), root); + : generatedFiles.includes(pathname.split('/').at(-1) ?? '') + ? new URL(pathname.split('/').at(-1) ?? '', generated) + : new URL(pathname.slice(1), root); void readFile(file).then((bytes) => { response.writeHead(200, { 'Content-Type': pathname.endsWith('.html') diff --git a/scripts/verify-package.ts b/tests/e2e/verify-package.ts similarity index 82% rename from scripts/verify-package.ts rename to tests/e2e/verify-package.ts index 80a8098..aa35c8b 100644 --- a/scripts/verify-package.ts +++ b/tests/e2e/verify-package.ts @@ -16,12 +16,12 @@ for (let index = 0; index < args.length; index += 2) { consumerNodes.add(executable); } -const root = fileURLToPath(new URL('../', import.meta.url)); +const root = fileURLToPath(new URL('../../', import.meta.url)); const temporary = await mkdtemp(join(tmpdir(), 'gothic-package-consumer-')); const artifacts = join(root, 'artifacts', 'package'); const destination = join(artifacts, 'package.tgz'); const checksumPath = join(artifacts, 'package.sha256'); -const compiler = fileURLToPath(new URL('../node_modules/typescript/bin/tsc', import.meta.url)); +const compiler = fileURLToPath(new URL('../../node_modules/typescript/bin/tsc', import.meta.url)); const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; function run(command: string, args: readonly string[], cwd = temporary): string { return execFileSync(command, args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'] }); @@ -52,20 +52,26 @@ try { const sourceFiles = (await readdir(repoDist)).sort(); assert.deepEqual((await readdir(packageDist)).sort(), sourceFiles); for (const file of sourceFiles) assert.deepEqual(await readFile(join(packageDist, file)), await readFile(join(repoDist, file)), `Pack changed ${file}`); - for (const file of ['consumer.mts', 'consumer.mjs', 'global-consumer.ts']) { - await copyFile(join(root, 'tests', 'distribution', 'fixtures', file), join(temporary, file)); + for (const file of ['types-consumer.ts', 'runtime-consumer.ts', 'global-consumer.ts']) { + await copyFile(join(root, 'tests', 'e2e', 'distribution', 'fixtures', file), join(temporary, file)); } const rawConfig: unknown = JSON.parse(await readFile(join(root, 'tsconfig.json'), 'utf8')); assert.ok(typeof rawConfig === 'object' && rawConfig !== null && 'compilerOptions' in rawConfig); assert.ok(typeof rawConfig.compilerOptions === 'object' && rawConfig.compilerOptions !== null); await writeFile(join(temporary, 'tsconfig.json'), JSON.stringify({ compilerOptions: { ...rawConfig.compilerOptions, types: [], lib: ['ES2022'], moduleDetection: 'legacy' }, - include: ['consumer.mts', 'global-consumer.ts'], + include: ['types-consumer.ts', 'global-consumer.ts'], })); run(process.execPath, [compiler, '--project', join(temporary, 'tsconfig.json')]); + await writeFile(join(temporary, 'tsconfig.runtime.json'), JSON.stringify({ + compilerOptions: { ...rawConfig.compilerOptions, types: ['node'], typeRoots: [join(root, 'node_modules', '@types')], noEmit: false, allowImportingTsExtensions: false }, + include: ['runtime-consumer.ts'], + })); + run(process.execPath, [compiler, '--project', join(temporary, 'tsconfig.runtime.json')]); for (const executable of consumerNodes) { - process.stdout.write(run(executable, [join(temporary, 'consumer.mjs')])); + process.stdout.write(run(executable, [join(temporary, 'runtime-consumer.js')])); } + run(process.execPath, [join(root, 'tests', 'e2e', 'build-fixtures.ts')], root); // Reuse byte-preserving HTTP and isolated file consumers against installed // tarball files. Vite and source resolution are absent from these pages. execFileSync(process.execPath, [join(root, 'node_modules', 'playwright', 'cli.js'), 'test'], { diff --git a/tests/unit/benchmark.test.ts b/tests/unit/benchmark.test.ts index d081c78..9279184 100644 --- a/tests/unit/benchmark.test.ts +++ b/tests/unit/benchmark.test.ts @@ -1,20 +1,24 @@ -import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { execFileSync } from 'node:child_process'; +import { mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { describe, expect, it } from 'vitest'; -import { array, commandMetrics, replayCommands, legacyCommands, record } from '../../benchmarks/validation.ts'; -import { compareMetrics, confirmPerformance, performanceVerdict, statistics } from '../../benchmarks/comparison.ts'; -import { catalogSha256, loadCatalog, sha256, verifySource, verifyReferenceSource } from '../../benchmarks/catalog.ts'; -import { publishReport, readSnapshot, runComparison, writeSnapshot, type Implementation } from '../../benchmarks/harness.ts'; +import { releaseAssets } from '../../.github/scripts/release-lib.ts'; +import { array, commandMetrics, replayCommands, legacyCommands, record } from '../benchmarks/validation.ts'; +import { compareMetrics, confirmPerformance, performanceVerdict, statistics } from '../benchmarks/comparison.ts'; +import { catalogSha256, loadCatalog, sha256 } from '../benchmarks/catalog.ts'; +import { publishReport, readSnapshot, runComparison, writeSnapshot, type Implementation } from '../benchmarks/harness.ts'; -import { parseBenchmarkArgs } from '../../benchmarks/cli.ts'; +import { inspectComparisonBaseline, inspectSource, validateSource } from '../benchmarks/source.ts'; +import { snapshotFromImplementation } from '../benchmarks/snapshot.ts'; +import { assertArtifactOutput, collectMetadata, parseBenchmarkArgs } from '../benchmarks/cli.ts'; const definition = { state: [3, 4], links: [[0, 0], [0, 0]] }; const commands: readonly (readonly [number, number])[] = [[0, 1]]; -const fixture = { id: 'unit', title: 'Unit', definition, expectedActions: 1, - baselineCommands: commands, historicalReference: { commands, A: 1, U: 1, C: 1, plateSwitches: 0 } }; +const fixture = { id: 'unit', ...definition, expectedActions: 1 }; +const source = { revision: '1'.repeat(40), moduleSha256: '2'.repeat(64), format: 'tuple' }; const implementation = (solve: Implementation['solve'] = () => commands): Implementation => ({ - label: 'unit', solve, normalize: (value) => value, requiresOptimality: true, + label: 'unit', solve, normalize: (value) => value, }); describe('benchmark replay independent of production transitions', () => { @@ -39,6 +43,7 @@ describe('deterministic and environmental verdicts stay separate', () => { it('fails every metric regression even if another metric improves', () => { expect(compareMetrics({ A: 4, U: 1, C: 6, plateSwitches: 0 }, { A: 3, U: 2, C: 8, plateSwitches: 1 }).quality).toBe('regression'); expect(compareMetrics({ A: 3, U: 3, C: 7, plateSwitches: 1 }, { A: 3, U: 2, C: 8, plateSwitches: 1 }).quality).toBe('regression'); + expect(compareMetrics({ A: 3, U: 2, C: 9, plateSwitches: 1 }, { A: 3, U: 2, C: 8, plateSwitches: 1 }).quality).toBe('regression'); expect(compareMetrics({ A: 3, U: 2, C: 8, plateSwitches: 2 }, { A: 3, U: 2, C: 8, plateSwitches: 1 }).quality).toBe('regression'); }); it('calibrates uncertainty from self-comparison and never uses historical times as proof', () => { @@ -63,29 +68,59 @@ describe('deterministic and environmental verdicts stay separate', () => { }); describe('evidence trust boundaries', () => { - it('retains all 45 immutable fixtures and original source pins', () => { - expect(loadCatalog()).toHaveLength(45); - expect(verifySource(new URL('../../benchmarks/baseline/', import.meta.url))).toHaveProperty('revision', 'a87e739a22ccc4215d6d9b14fa06b0738171cb60'); - expect(verifySource(new URL('../../benchmarks/reference-matrix/', import.meta.url))).toHaveProperty('revision', '6b1cfbc13bcec68f609d45d5dc97504dd84f1760'); + it('retains only all 45 fixed numerical fixtures and mathematical expectations', () => { + const locks = loadCatalog(); + expect(locks).toHaveLength(45); + expect(Object.keys(locks[0] ?? {}).sort()).toEqual(['expectedActions', 'id', 'links', 'state']); + expect(locks.reduce((sum, lock) => sum + lock.expectedActions, 0)).toBe(483); }); - it('rejects source changes before external code can execute', async () => { + it('rejects missing source identity and mismatched module, format or revision', async () => { + expect(() => validateSource({})).toThrow(/source|baseline/i); + expect(() => inspectSource('/missing/own-baseline.ts', 'tuple')).toThrow(); const root = await mkdtemp(join(tmpdir(), 'benchmark-source-')); + const module = join(root, 'solver.ts'); try { - await writeFile(join(root, 'solver.mjs'), 'original'); - await writeFile(join(root, 'manifest.json'), JSON.stringify({ revision: 'test', files: { 'solver.mjs': sha256('original') } })); - verifySource(root); - await writeFile(join(root, 'solver.mjs'), 'modified'); - expect(() => verifySource(root)).toThrow(/hash/i); + await writeFile(module, 'export function solveLock() { return [[0, 1]]; }\n'); + expect(() => inspectSource(module, 'tuple')).toThrow(/own Git checkout|identity/); + expect(() => inspectSource(module, 'tuple', source)).toThrow(/hash/); + const pinned = { ...source, moduleSha256: sha256(await readFile(module)) }; + expect(() => inspectSource(module, 'tuple', pinned)).toThrow(/verified release/); + expect(() => inspectSource(module, 'legacy', pinned)).toThrow(/format/); + execFileSync('git', ['init', '--quiet', root]); + execFileSync('git', ['-C', root, 'add', '.']); + execFileSync('git', ['-C', root, '-c', 'user.name=Benchmark Test', '-c', 'user.email=benchmark@example.invalid', 'commit', '--quiet', '-m', 'Own baseline']); + expect(() => inspectSource(module, 'tuple', pinned)).toThrow(/revision/); } finally { await rm(root, { recursive: true, force: true }); } }); - it('checks upstream revision, entire file and marked block independently', () => { - const block = '/* solver-start */ function solveLock() {} /* solver-end */'; - const html = ``; - const pin = { revision: 'pin', solver: { path: 'index.html', sha256: sha256(html), blockSha256: sha256(block), startMarker: '/* solver-start', endMarker: '/* solver-end */' } }; - expect(verifyReferenceSource({ revision: 'pin', html }, pin)).toBe(block); - expect(() => verifyReferenceSource({ revision: 'bad', html }, pin)).toThrow(/revision/i); - expect(() => verifyReferenceSource({ revision: 'pin', html: 'modified' }, pin)).toThrow(/hash/i); - expect(() => verifyReferenceSource({ revision: 'pin', html }, { ...pin, solver: { ...pin.solver, blockSha256: 'wrong' } })).toThrow(/block hash/i); + it('pins every own checkout source file directly to Git without copying it', async () => { + const root = await mkdtemp(join(tmpdir(), 'benchmark-source-')); + try { + await mkdir(join(root, 'src')); + const module = join(root, 'src', 'solver.ts'); + const dependency = join(root, 'src', 'dependency.ts'); + await writeFile(module, 'export function solveLock() { return [[0, 1]]; }\n'); + await writeFile(dependency, 'export const value = 1;\n'); + execFileSync('git', ['init', '--quiet', root]); + execFileSync('git', ['-C', root, 'add', '.']); + execFileSync('git', ['-C', root, '-c', 'user.name=Benchmark Test', '-c', 'user.email=benchmark@example.invalid', 'commit', '--quiet', '-m', 'Own baseline']); + const pinned = inspectSource(module, 'tuple'); + expect(inspectSource(module, 'tuple', pinned)).toEqual(pinned); + await writeFile(dependency, 'export const value = 1;\n\n'); + expect(() => inspectSource(module, 'tuple')).toThrow(/source hash/); + expect(() => inspectSource(module, 'tuple', pinned)).toThrow(/tree hash/); + await writeFile(dependency, 'export const value = 1;\n'); + await writeFile(join(root, 'src', 'untracked.ts'), 'export const value = 2;\n'); + expect(() => inspectSource(module, 'tuple')).toThrow(/untracked source/); + } finally { await rm(root, { recursive: true, force: true }); } + }); + it('generates snapshot quality by executing only the explicitly supplied baseline', () => { + let calls = 0; + const snapshot = snapshotFromImplementation(implementation(() => { calls += 1; return commands; }), source, 'own baseline', [fixture]); + expect(calls).toBe(1); + expect(snapshot.source).toEqual(source); + expect(snapshot.locks).toEqual([{ id: 'unit', optimumA: 1, ...commandMetrics(commands), commands }]); + expect(() => snapshotFromImplementation(implementation(() => []), source, 'own baseline', [fixture])).toThrow(/target/); + expect(() => snapshotFromImplementation(implementation(), {}, 'own baseline', [fixture])).toThrow(/source|baseline/); }); it('never overwrites a quality snapshot', async () => { const root = await mkdtemp(join(tmpdir(), 'benchmark-snapshot-')); @@ -108,18 +143,30 @@ describe('evidence trust boundaries', () => { }); it('requires explicit baseline inputs and rejects unsafe or ambiguous CLI settings', () => { - expect(() => parseBenchmarkArgs([], true)).toThrow(/explicit/); - expect(parseBenchmarkArgs(['--baseline-module', '/base.mjs', '--baseline-snapshot', '/quality.json', '--baseline-format', 'legacy'], true).baselineFormat).toBe('legacy'); + expect(() => parseBenchmarkArgs([])).toThrow(/explicit/); + expect(parseBenchmarkArgs(['--baseline-module', '/base.mjs', '--baseline-format', 'legacy']).outputDir).toMatch(/artifacts\/benchmark$/); + expect(parseBenchmarkArgs(['--baseline-module', '/base.mjs', '--baseline-format', 'legacy']).baselineSnapshot).toBeUndefined(); + expect(parseBenchmarkArgs(['--baseline-module', '/base.mjs', '--baseline-snapshot', '/quality.json', '--baseline-format', 'legacy']).baselineFormat).toBe('legacy'); for (const args of [['--warmups', '-1'], ['--warmups', '01'], ['--repetitions', '0'], ['--warmups', '1', '--warmups', '2'], ['--reference'], ['--unknown', '1']]) { expect(() => parseBenchmarkArgs(args)).toThrow(/argument|integer/); } }); +it('rejects unsupported comparison slots', () => { + expect(() => parseBenchmarkArgs(['--reference', '/separate-checkout'])).toThrow(/Unknown argument/); +}); + +it('reports only measurements of the supplied project implementations', () => { + const report = runComparison({ locks: [fixture], candidate: implementation(), baseline: implementation(), repetitions: 1, warmups: 0 }); + expect(Object.keys(report.implementations)).toEqual(['candidate', 'baseline']); + expect(report.results[0]).not.toHaveProperty('historicalReference'); +}); + it('checks target-branch snapshot against the live base independently of candidate expectations', () => { const baselineSnapshot = { schemaVersion: 1, fixtureSha256: catalogSha256, source: {}, reason: 'test baseline', locks: [{ id: 'unit', optimumA: 1, ...commandMetrics(commands), commands: [[0, 2], [0, -1]] as const }] }; expect(() => runComparison({ locks: [fixture], candidate: implementation(), baseline: implementation(), baselineSnapshot, repetitions: 1, warmups: 0 })).toThrow(/snapshot path drift/); - expect(readSnapshot(new URL('../../benchmarks/snapshots/reference-matrix.json', import.meta.url).pathname).locks).toHaveLength(45); + }); it('publishes report, Markdown and hash-linked release evidence without changing earlier reports', async () => { @@ -139,17 +186,153 @@ it('publishes report, Markdown and hash-linked release evidence without changing }); it('isolated RSS worker restricts its workload to the selected smoke fixtures', async () => { - const { measureRss } = await import('../../benchmarks/memory.ts'); - const result = measureRss({ name: 'baseline', module: new URL('../../benchmarks/reference-matrix/src/index.mjs', import.meta.url).pathname, format: 'legacy' }, 'cor-galom-chest,cor-galom-bedroom-chest'); + const { measureRss } = await import('../benchmarks/memory.ts'); + const result = measureRss({ name: 'baseline', module: new URL('../../src/index.ts', import.meta.url).pathname, format: 'tuple' }, loadCatalog().slice(0, 2).map((lock) => lock.id).join(',')); expect(result.lockCount).toBe(2); expect(result.peakRssBytes).toBeGreaterThan(0); expect(result.startupRssBytes).toBeGreaterThan(0); }); it('names the process-image RSS metric and preserves launch-floor diagnostics', async () => { - const { measureRss } = await import('../../benchmarks/memory.ts'); - const result = measureRss({ name: 'baseline', module: new URL('../../benchmarks/reference-matrix/src/index.mjs', import.meta.url).pathname, format: 'legacy' }, 'cor-galom-chest'); + const { measureRss } = await import('../benchmarks/memory.ts'); + const result = measureRss({ name: 'baseline', module: new URL('../../src/index.ts', import.meta.url).pathname, format: 'tuple' }, loadCatalog()[0]?.id); expect(result.peakRssMetric).toBe(process.platform === 'linux' ? 'linux-proc-VmHWM' : 'node-resourceUsage-maxRSS'); expect(result.startupPeakRssBytes).toBeGreaterThan(0); expect(result.resourceUsageMaxRssBytes).toBeGreaterThan(0); }); + + +it('requires ignored artifact output paths and rejects snapshots with missing identity', async () => { + expect(() => assertArtifactOutput('docs/benchmarks')).toThrow(/ignored artifacts/); + expect(() => assertArtifactOutput('artifacts/../tests')).toThrow(/ignored artifacts/); + expect(() => assertArtifactOutput('artifacts/benchmark')).not.toThrow(); + const root = await mkdtemp(join(tmpdir(), 'benchmark-snapshot-')); + try { + const path = join(root, 'snapshot.json'); + await writeSnapshot(path, { schemaVersion: 1, fixtureSha256: catalogSha256, source: {}, reason: 'missing identity', locks: [] }); + expect(() => readSnapshot(path)).toThrow(/source|identity|baseline/); + } finally { await rm(root, { recursive: true, force: true }); } +}); + + +it('retains both complete raw timing attempts when a matched slowdown repeats', () => { + let elapsed = 0; + const order: string[] = []; + const measured = (name: string, duration: number) => implementation(() => { order.push(name); elapsed += duration; return commands; }); + const report = runComparison({ locks: [fixture], candidate: measured('candidate', 2), baseline: measured('baseline', 1), repetitions: 5, warmups: 0, clock: () => elapsed }); + const row = report.results[0]; + expect(row?.timing.verdict).toBe('regression'); + expect(row?.timingAttempts).toHaveLength(2); + for (const attempt of row?.timingAttempts ?? []) { + expect(attempt.candidate).toEqual([2, 2, 2, 2, 2]); + expect(attempt.baseline).toEqual([1, 1, 1, 1, 1]); + expect(attempt.selfA).toEqual([1, 1, 1, 1, 1]); + expect(attempt.selfB).toEqual([1, 1, 1, 1, 1]); + } + expect(order.slice(10, 14)).toEqual(['candidate', 'baseline', 'baseline', 'candidate']); +}); + +async function makeTypedCheckout() { + const directory = await mkdtemp(join(tmpdir(), 'benchmark-typed-base-')); + await mkdir(join(directory, 'src')); + await mkdir(join(directory, 'dist')); + await writeFile(join(directory, '.gitignore'), 'dist/\n'); + const trackedModule = join(directory, 'src', 'index.ts'); + await writeFile(trackedModule, 'export function solveLock() { return [[0, 1]]; }\n'); + execFileSync('git', ['init', '--quiet', directory]); + execFileSync('git', ['-C', directory, 'add', '.']); + execFileSync('git', ['-C', directory, '-c', 'user.name=Benchmark Test', '-c', 'user.email=benchmark@example.invalid', 'commit', '--quiet', '-m', 'Own typed source']); + const revision = execFileSync('git', ['-C', directory, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(); + const module = join(directory, 'dist', 'gothic-lock-solver.mjs'); + await writeFile(module, 'export function solveLock() { return [[0, 1]]; }\n'); + const manifest = { schemaVersion: 1, package: 'gothic-lock-solver', version: '1.0.0', sourceSha: revision, + files: { 'gothic-lock-solver.mjs': sha256(await readFile(module)) } }; + return { directory, trackedModule, module, manifest, manifestPath: join(directory, 'dist', 'manifest.json') }; +} + +describe('typed baseline build provenance', () => { + it.each(['missing manifest', 'stale revision', 'wrong module checksum'] as const)('rejects %s in an otherwise clean checkout', async (problem) => { + const checkout = await makeTypedCheckout(); + try { + if (problem !== 'missing manifest') await writeFile(checkout.manifestPath, JSON.stringify({ ...checkout.manifest, + ...(problem === 'stale revision' ? { sourceSha: '0'.repeat(40) } : { files: { 'gothic-lock-solver.mjs': '0'.repeat(64) } }) })); + expect(() => inspectSource(checkout.module, 'tuple')).toThrow(/manifest|revision|checksum|hash/); + } finally { await rm(checkout.directory, { recursive: true, force: true }); } + }); + it('accepts the module whose manifest pins its checkout revision and bytes', async () => { + const checkout = await makeTypedCheckout(); + try { + await writeFile(checkout.manifestPath, JSON.stringify(checkout.manifest)); + expect(inspectSource(checkout.module, 'tuple')).toMatchObject({ revision: checkout.manifest.sourceSha, moduleSha256: checkout.manifest.files['gothic-lock-solver.mjs'] }); + } finally { await rm(checkout.directory, { recursive: true, force: true }); } + }); + it('rejects a baseline module from the candidate checkout before measuring', async () => { + const checkout = await makeTypedCheckout(); + try { + await writeFile(checkout.manifestPath, JSON.stringify(checkout.manifest)); + const baseline = inspectSource(checkout.trackedModule, 'tuple'); + const snapshot = snapshotFromImplementation(implementation(), baseline, 'test', [fixture]); + const options = parseBenchmarkArgs(['--candidate-module', checkout.module, '--baseline-module', checkout.trackedModule]); + expect(() => collectMetadata(options, snapshot)).toThrow(/same.*checkout|candidate.*checkout/); + } finally { await rm(checkout.directory, { recursive: true, force: true }); } + }); + it('rejects a separate checkout at the candidate revision', async () => { + const checkout = await makeTypedCheckout(); + const copy = await mkdtemp(join(tmpdir(), 'benchmark-same-revision-')); + try { + execFileSync('git', ['clone', '--quiet', checkout.directory, copy]); + const baselineModule = join(copy, 'src', 'index.ts'); + const baseline = inspectSource(baselineModule, 'tuple'); + const snapshot = snapshotFromImplementation(implementation(), baseline, 'test', [fixture]); + const options = parseBenchmarkArgs(['--candidate-module', checkout.trackedModule, '--baseline-module', baselineModule]); + expect(() => collectMetadata(options, snapshot)).toThrow(/same.*revision|candidate.*revision/); + } finally { await rm(checkout.directory, { recursive: true, force: true }); await rm(copy, { recursive: true, force: true }); } + }); +}); + + +it('rejects a symlink alias of the candidate module', async () => { + const checkout = await makeTypedCheckout(); + const aliasRoot = await mkdtemp(join(tmpdir(), 'benchmark-module-alias-')); + try { + const alias = join(aliasRoot, 'baseline.ts'); + await symlink(checkout.trackedModule, alias); + expect(() => inspectComparisonBaseline(checkout.trackedModule, alias, 'tuple')).toThrow(/same module/); + } finally { await rm(checkout.directory, { recursive: true, force: true }); await rm(aliasRoot, { recursive: true, force: true }); } +}); + +it('accepts identical module bytes from a separate checkout at a different target revision', async () => { + const checkout = await makeTypedCheckout(); + const copy = await mkdtemp(join(tmpdir(), 'benchmark-other-revision-')); + try { + execFileSync('git', ['clone', '--quiet', checkout.directory, copy]); + execFileSync('git', ['-C', copy, '-c', 'user.name=Benchmark Test', '-c', 'user.email=benchmark@example.invalid', 'commit', '--quiet', '--allow-empty', '-m', 'Another target revision']); + const baseline = join(copy, 'src', 'index.ts'); + expect(inspectComparisonBaseline(checkout.trackedModule, baseline, 'tuple')).toHaveProperty('moduleSha256', sha256(await readFile(checkout.trackedModule))); + } finally { await rm(checkout.directory, { recursive: true, force: true }); await rm(copy, { recursive: true, force: true }); } +}); + +it('allows a same-revision preceding release only with its verified manifest and evidence chain', async () => { + const checkout = await makeTypedCheckout(); + try { + const release = join(checkout.directory, 'artifacts', 'baseline'); + await mkdir(release, { recursive: true }); + const module = join(release, 'baseline.mjs'); + await writeFile(module, await readFile(checkout.module)); + const originalSource = { revision: checkout.manifest.sourceSha, moduleSha256: sha256(await readFile(module)), format: 'tuple' }; + const evidenceBytes = JSON.stringify({ qualitySnapshot: { source: originalSource } }); + const evidenceHash = sha256(evidenceBytes); + const assets = { ...Object.fromEntries(releaseAssets.map((name) => [name, 'a'.repeat(64)])), + 'gothic-lock-solver.mjs': originalSource.moduleSha256, 'benchmark-evidence.json': evidenceHash }; + const manifestBytes = JSON.stringify({ schemaVersion: 1, name: 'gothic-lock-solver', version: '1.0.0', sourceSha: originalSource.revision, + kind: 'stable', tarballSha256: 'a'.repeat(64), assets }); + const pinned = { ...originalSource, kind: 'previous-stable-release', releaseVersion: '1.0.0', + releaseManifestSha256: sha256(manifestBytes), releaseEvidenceSha256: evidenceHash }; + expect(() => inspectComparisonBaseline(checkout.trackedModule, module, 'tuple', pinned)).toThrow(/manifest/); + await writeFile(join(release, 'previous-release-manifest.json'), manifestBytes); + await writeFile(join(release, 'previous-benchmark-evidence.json'), evidenceBytes); + expect(inspectComparisonBaseline(checkout.trackedModule, module, 'tuple', pinned)).toMatchObject(pinned); + await writeFile(join(release, 'previous-benchmark-evidence.json'), `${evidenceBytes} `); + expect(() => inspectComparisonBaseline(checkout.trackedModule, module, 'tuple', pinned)).toThrow(/evidence hash/); + } finally { await rm(checkout.directory, { recursive: true, force: true }); } +}); diff --git a/tests/unit/cli.test.ts b/tests/unit/cli.test.ts index b30878d..3998162 100644 --- a/tests/unit/cli.test.ts +++ b/tests/unit/cli.test.ts @@ -2,7 +2,7 @@ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { runCli } from '../../cli/cli.ts'; +import { runCli } from '../../src/cli.ts'; const directories: string[] = []; afterEach(async () => { @@ -57,14 +57,99 @@ describe('CLI file adapter', () => { expect(result).toEqual({ status: 'unsolvable', initialState: [1, 7], targetState: [4, 4], commands: [] }); }); - it.each([[], [''], ['--unknown'], ['x', '--output'], ['x', 'extra'], ['x', '--output', ''], ['x', '--output', 'y', 'extra']])('rejects malformed command line %j', async (...args) => { + it.each([ + {}, + { maxVisited: 2_000_000, maxExpanded: 1_000_000, maxFrontier: 1_000_000, maxDenseBytes: 67_108_864 }, + { maxDenseBytes: 0 }, + ])('uses defaults for omitted settings in configuration %j', async (config) => { + const files = await fixture({ state: [1, 7], links: [[0, 0], [0, 0]] }); + const configPath = join(files.directory, 'solver.config.json'); + const contents = JSON.stringify(config); + await writeFile(configPath, contents); + expect(await run([files.input, '--config', configPath])).toEqual({ + code: 0, + stdout: 'Найдено команд: 2\n1. Пластина 1 - влево x3\n2. Пластина 2 - вправо x3\n', + stderr: '', + }); + expect(await readFile(configPath, 'utf8')).toBe(contents); + }); + + it('applies a partial configuration and reports exhaustion as an error', async () => { + const files = await fixture({ state: [3, 1, 1], links: [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]] }); + const configPath = join(files.directory, 'solver.config.json'); + await writeFile(configPath, JSON.stringify({ maxExpanded: 0 })); + expect(await run([files.input, '--config', configPath, '--output', files.output])).toMatchObject({ + code: 1, stdout: '', stderr: expect.stringContaining('maxExpanded'), + }); + await expect(readFile(files.output)).rejects.toMatchObject({ code: 'ENOENT' }); + }); + + it('accepts output before configuration and preserves the JSON result', async () => { + const files = await fixture({ state: [4, 4], links: [[0, 0], [0, 0]] }); + const configPath = join(files.directory, 'solver.config.json'); + await writeFile(configPath, JSON.stringify({ maxExpanded: 0 })); + expect(await run([files.input, '--output', files.output, '--config', configPath])).toEqual({ + code: 0, stdout: 'Замок уже открыт.\n', stderr: '', + }); + const output: unknown = JSON.parse(await readFile(files.output, 'utf8')); + expect(output).toEqual({ + status: 'solved', initialState: [4, 4], targetState: [4, 4], commands: [], finalState: [4, 4], + metrics: { commands: 0, divisions: 0, plateSwitches: 0 }, + }); + }); + + it.each([null, [], 1, 'config', { unknown: 1 }, { maxVisited: '10' }, { maxExpanded: null }, { maxFrontier: 0 }].map((config) => ({ config })))( + 'rejects an invalid configuration $config without running the solver', async ({ config }) => { + const files = await fixture({ state: [4, 4], links: [[0, 0], [0, 0]] }); + const configPath = join(files.directory, 'solver.config.json'); + await writeFile(configPath, JSON.stringify(config)); + const result = await run([files.input, '--config', configPath]); + expect(result).toMatchObject({ code: 1, stdout: '' }); + expect(result.stderr).toMatch(/^Ошибка:/u); + expect(result.stderr).not.toContain('Использование'); + }, + ); + + it('reports a missing configuration file', async () => { + const files = await fixture({ state: [4, 4], links: [[0, 0], [0, 0]] }); + const configPath = join(files.directory, 'solver.config.json'); + expect(await run([files.input, '--config', configPath])).toMatchObject({ + code: 1, stdout: '', stderr: expect.stringContaining('ENOENT'), + }); + }); + + it('reports malformed configuration JSON', async () => { + const files = await fixture({ state: [4, 4], links: [[0, 0], [0, 0]] }); + const configPath = join(files.directory, 'solver.config.json'); + await writeFile(configPath, '{'); + expect(await run([files.input, '--config', configPath])).toMatchObject({ + code: 1, stdout: '', stderr: expect.stringContaining('Некорректный JSON'), + }); + }); + + it('prints help successfully without requiring an input file', async () => { + const result = await run(['--help']); + expect(result.code).toBe(0); + expect(result.stderr).toBe(''); + expect(result.stdout).toMatch(/^Использование:/u); + expect(result.stdout).toContain('--config'); + expect(result.stdout).toContain('--output'); + }); + + it.each([ + [], [''], ['--unknown'], ['x', '--output'], ['x', 'extra'], ['x', '--output', ''], + ['x', '--output', 'y', 'extra'], ['x', '--config'], ['x', '--config', ''], + ['x', '--config', '--output', 'y'], ['x', '--unknown', 'y'], + ['x', '--config', 'a', '--config', 'b'], ['x', '--output', 'a', '--output', 'b'], + ['--help', '--unknown'], ['x', '--help'], + ])('rejects malformed command line %j', async (...args) => { const result = await run(args); expect(result.code).toBe(1); expect(result.stdout).toBe(''); expect(result.stderr).toMatch(/^Ошибка: Использование:/u); }); - it.each([null, [], {}, { state: [0, 4], links: [[0, 0], [0, 0]] }])('rejects malformed input %j without reporting unsolvable', async (input) => { + it.each([null, [], {}, { state: [0, 4], links: [[0, 0], [0, 0]] }].map((input) => ({ input })))('rejects malformed input $input without reporting unsolvable', async ({ input }) => { const files = await fixture(input); const result = await run([files.input]); expect(result.code).toBe(1); diff --git a/tests/unit/config.test.ts b/tests/unit/config.test.ts new file mode 100644 index 0000000..cc25e7e --- /dev/null +++ b/tests/unit/config.test.ts @@ -0,0 +1,133 @@ +import { describe, expect, it } from 'vitest'; +import * as api from '../../src/index.ts'; +import type { SolverConfig } from '../../src/index.ts'; + +const defaults = { + maxVisited: 2_000_000, + maxExpanded: 1_000_000, + maxFrontier: 1_000_000, + maxDenseBytes: 67_108_864, +}; +const state = [1, 7] as const; +const links = [[0, 0], [0, 0]] as const; + +function configFromJavaScript(value: unknown): unknown { + return Reflect.apply(api.createSolverConfig, undefined, [value]); +} + +function solveFromJavaScript(value: unknown): unknown { + return Reflect.apply(api.solveLock, undefined, [state, links, value]); +} + +describe('public solver configuration', () => { + it('exports a factory that returns complete frozen defaults', () => { + expect(api).toHaveProperty('createSolverConfig'); + const config = api.createSolverConfig(); + expect(config).toEqual(defaults); + expect(Object.isFrozen(config)).toBe(true); + expect(api.createSolverConfig(undefined)).toEqual(defaults); + expect(api.solveLock(state, links, config)).toEqual([[0, 3], [1, -3]]); + expect(api.solveLock(state, links)).toEqual([[0, 3], [1, -3]]); + }); + + it('copies partial overrides without freezing or mutating the caller', () => { + const overrides = { maxVisited: 3, maxDenseBytes: 0 }; + const config = api.createSolverConfig(overrides); + expect(config).toEqual({ ...defaults, maxVisited: 3, maxDenseBytes: 0 }); + expect(overrides).toEqual({ maxVisited: 3, maxDenseBytes: 0 }); + expect(Object.isFrozen(overrides)).toBe(false); + overrides.maxVisited = 1; + expect(config.maxVisited).toBe(3); + expect(Reflect.set(config, 'maxVisited', 1)).toBe(false); + expect(api.solveLock(state, links, config)).toEqual([[0, 3], [1, -3]]); + }); + + it('accepts inclusive integer bounds and zero for expansion and dense storage', () => { + expect(api.createSolverConfig({ maxVisited: 1, maxFrontier: 1, maxExpanded: 0, maxDenseBytes: 0 })) + .toEqual({ maxVisited: 1, maxFrontier: 1, maxExpanded: 0, maxDenseBytes: 0 }); + const maximum = Object.fromEntries(Object.keys(defaults).map((key) => [key, Number.MAX_SAFE_INTEGER])); + expect(configFromJavaScript(maximum)).toEqual(maximum); + }); + + it.each([null, [], 1, 'config', true, { unknown: 1 }, { [Symbol('unknown')]: 1 }].map((value) => ({ value }))) + ('rejects invalid override objects and unknown fields: $value', ({ value }) => { + expect(() => configFromJavaScript(value)).toThrow(api.LockInputError); + }); + + for (const field of Object.keys(defaults)) { + it.each([undefined, null, true, '1', 1n, -1, 0.5, Number.NaN, Infinity, Number.MAX_SAFE_INTEGER + 1]) + (`rejects invalid ${field} values: %s`, (value) => { + expect(() => configFromJavaScript({ [field]: value })).toThrow(api.LockInputError); + expect(() => solveFromJavaScript({ ...defaults, [field]: value })).toThrow(api.LockInputError); + }); + } + + it('rejects zero visited/frontier bounds and nonenumerable unknown fields', () => { + for (const field of ['maxVisited', 'maxFrontier']) { + expect(() => configFromJavaScript({ [field]: 0 })).toThrow(api.LockInputError); + } + expect(() => configFromJavaScript(Object.defineProperty({}, 'unknown', { value: 1 }))).toThrow(api.LockInputError); + }); + + it.each([null, [], false, {}, { maxVisited: 3 }, { ...defaults, unknown: 1 }].map((value) => ({ value }))) + ('revalidates complete configs from JavaScript: $value', ({ value }) => { + expect(() => solveFromJavaScript(value)).toThrow(api.LockInputError); + }); + + it('requires own complete fields and validates ordinary caller mutations', () => { + const inherited: unknown = Object.create(defaults); + expect(() => solveFromJavaScript(inherited)).toThrow(api.LockInputError); + const ordinary = { ...defaults }; + expect(solveFromJavaScript(ordinary)).toEqual([[0, 3], [1, -3]]); + expect(ordinary).toEqual(defaults); + expect(Object.isFrozen(ordinary)).toBe(false); + ordinary.maxExpanded = -1; + expect(() => solveFromJavaScript(ordinary)).toThrow(api.LockInputError); + }); +}); + +describe('public search budgets', () => { + const cases = [ + { name: 'certificate', state, links }, + { name: 'A*', state: [3, 1, 1] as const, links: [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]] as const }, + { name: 'singular BFS', state: [1, 1] as const, links: [[0, 1], [1, 0]] as const }, + ]; + for (const lock of cases) { + for (const overrides of [{ maxExpanded: 0 }, { maxVisited: 1 }]) { + it(`${lock.name} reports ${Object.keys(overrides)[0]} exhaustion instead of null`, () => { + const config = api.createSolverConfig(overrides); + expect(() => api.solveLock(lock.state, lock.links, config)).toThrow(api.SearchLimitError); + }); + } + } + + it('enforces frontier limits in both exact search strategies', () => { + for (const lock of cases.slice(1)) { + expect(() => api.solveLock(lock.state, lock.links, api.createSolverConfig({ maxFrontier: 1 }))) + .toThrow(api.SearchLimitError); + } + }); + + it('counts the certificate path and permits exactly sufficient budgets', () => { + const overrides: Partial = { maxVisited: 3, maxExpanded: 2, maxFrontier: 1 }; + expect(api.solveLock(state, links, api.createSolverConfig(overrides))).toEqual([[0, 3], [1, -3]]); + expect(() => api.solveLock(state, links, api.createSolverConfig({ ...overrides, maxVisited: 2 }))) + .toThrow(api.SearchLimitError); + expect(() => api.solveLock(state, links, api.createSolverConfig({ ...overrides, maxExpanded: 1 }))) + .toThrow(api.SearchLimitError); + }); + + it('preserves singular BFS action order in dense and forced sparse storage', () => { + const singular = [[0, 1, 0], [1, 0, 0], [0, 0, 0]] as const; + for (const maxDenseBytes of [0, defaults.maxDenseBytes]) { + expect(api.solveLock([1, 1, 7], singular, api.createSolverConfig({ maxDenseBytes }))) + .toEqual([[0, 3], [2, -3]]); + } + }); + + it('allows open and algebraically unreachable locks without state expansion', () => { + const config = api.createSolverConfig({ maxExpanded: 0 }); + expect(api.solveLock([4, 4], links, config)).toEqual([]); + expect(api.solveLock([3, 4], [[0, 1], [-1, 0]], config)).toBeNull(); + }); +}); diff --git a/tests/unit/core-contract.test.ts b/tests/unit/core-contract.test.ts index 53d8dc7..8ea9d31 100644 --- a/tests/unit/core-contract.test.ts +++ b/tests/unit/core-contract.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import * as api from '../../src/index.ts'; import type { Link, Position } from '../../src/index.ts'; -import { referenceActions, replay, unitMove } from '../helpers/oracle.ts'; +import { referenceActions, replay, unitMove } from './oracle.ts'; const inverse = [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]] as const; const independent = (state: readonly Position[]) => state.map(() => state.map((): Link => 0)); @@ -11,9 +11,8 @@ function solveUnknown(state: unknown, links: unknown): unknown { } describe('public tuple contract', () => { - it('exports only the named solver and documented error classes', () => { - expect(Object.keys(api).sort()).toEqual(['LockInputError', 'SearchLimitError', 'solveLock']); - expect(api.solveLock.length).toBe(2); + it('exports the named solver, config factory and documented error classes', () => { + expect(Object.keys(api).sort()).toEqual(['LockInputError', 'SearchLimitError', 'createSolverConfig', 'solveLock']); }); it('uses numeric pin delta and links[source][target] without reverse influence', () => { expect(api.solveLock([6, 2], [[0, -1], [0, 0]])).toEqual([[0, -2]]); diff --git a/tests/types/core-contract.ts b/tests/unit/core-contract.types.ts similarity index 95% rename from tests/types/core-contract.ts rename to tests/unit/core-contract.types.ts index df0857a..6656b04 100644 --- a/tests/types/core-contract.ts +++ b/tests/unit/core-contract.types.ts @@ -21,7 +21,7 @@ solveLock(state, [[0], [0]]); solveLock(state, [[0, 2], [0, 0]]); // @ts-expect-error old object wrapper is not public solveLock({ state, links }); -// @ts-expect-error public solver has no third options argument +// @ts-expect-error unknown algorithm selectors are not solver configuration solveLock(state, links, { algorithm: 'bfs' }); // @ts-expect-error zero delta is not a command const invalidDelta: Delta = 0; diff --git a/tests/unit/core-limits.test.ts b/tests/unit/core-limits.test.ts index 6d10b3e..cf68c8c 100644 --- a/tests/unit/core-limits.test.ts +++ b/tests/unit/core-limits.test.ts @@ -1,10 +1,12 @@ import { expect, it } from 'vitest'; import { SearchLimitError } from '../../src/index.ts'; -import { LockModel } from '../../src/lock-model.ts'; -import { PreparedSearch, SearchBudget, movementLimits } from '../../src/search-limits.ts'; -import { BfsSearch } from '../../src/search-bfs.ts'; -import { MatrixSearch } from '../../src/matrix-search.ts'; -import { analyzeMatrix } from '../../src/matrix-analysis.ts'; +import { LockModel } from '../../src/lock.ts'; +import { PreparedSearch, movementLimits } from '../../src/lock.ts'; +import { SearchBudget } from '../../src/astar.ts'; +import { createSolverConfig } from '../../src/index.ts'; +import { BfsSearch } from '../../src/bfs.ts'; +import { MatrixSearch } from '../../src/astar.ts'; +import { analyzeMatrix } from '../../src/matrix.ts'; it('reports exhaustion separately on BFS, A*, singular fallback and greedy certificate', () => { const inputs = [ @@ -15,10 +17,10 @@ it('reports exhaustion separately on BFS, A*, singular fallback and greedy certi for (const model of inputs) { const prepared = new PreparedSearch(model); for (const options of [{ maxExpanded: 0 }, { maxVisited: 1 }]) { - expect(() => new MatrixSearch(model, prepared, new SearchBudget(options)).solve()).toThrow(SearchLimitError); - expect(() => new BfsSearch(prepared, new SearchBudget(options)).solve()).toThrow(SearchLimitError); + expect(() => new MatrixSearch(model, prepared, new SearchBudget(createSolverConfig(options))).solve()).toThrow(SearchLimitError); + expect(() => new BfsSearch(prepared, new SearchBudget(createSolverConfig(options))).solve()).toThrow(SearchLimitError); } - expect(() => new BfsSearch(prepared, new SearchBudget({ maxFrontier: 1 })).solve()).toThrow(SearchLimitError); + expect(() => new BfsSearch(prepared, new SearchBudget(createSolverConfig({ maxFrontier: 1 }))).solve()).toThrow(SearchLimitError); } }); it('dense and sparse BFS preserve exact action order, including wider safe codes', () => { @@ -26,10 +28,10 @@ it('dense and sparse BFS preserve exact action order, including wider safe codes const prepared = new PreparedSearch(model); const expected = [[0, 5], [2, 3]]; expect(new BfsSearch(prepared, new SearchBudget()).solve()).toEqual(expected); - expect(new BfsSearch(prepared, new SearchBudget({ maxDenseBytes: 0 })).solve()).toEqual(expected); + expect(new BfsSearch(prepared, new SearchBudget(createSolverConfig({ maxDenseBytes: 0 }))).solve()).toEqual(expected); const state = [1, ...Array.from({ length: 17 }, () => 4)]; const wide = new PreparedSearch(new LockModel(state, state.map(() => state.map(() => 0)))); - expect(new BfsSearch(wide, new SearchBudget({ maxDenseBytes: 0, maxVisited: 5, maxExpanded: 1, maxFrontier: 4 })).solve()).toEqual([[0, 3]]); + expect(new BfsSearch(wide, new SearchBudget(createSolverConfig({ maxDenseBytes: 0, maxVisited: 5, maxExpanded: 1, maxFrontier: 4 }))).solve()).toEqual([[0, 3]]); }); it('distinguishes exact rational proofs from rank deficiency', () => { expect(analyzeMatrix(new LockModel([3, 4], [[0, 1], [-1, 0]])).kind).toBe('noninteger'); diff --git a/tests/unit/core-oracle.test.ts b/tests/unit/core-oracle.test.ts index cc8d289..c8611b4 100644 --- a/tests/unit/core-oracle.test.ts +++ b/tests/unit/core-oracle.test.ts @@ -1,13 +1,13 @@ import { describe, expect, it } from 'vitest'; import { solveLock } from '../../src/index.ts'; import type { Link, Position } from '../../src/index.ts'; -import { LockModel } from '../../src/lock-model.ts'; -import { PreparedSearch, SearchBudget } from '../../src/search-limits.ts'; -import { BfsSearch } from '../../src/search-bfs.ts'; -import { referenceActions, replay } from '../helpers/oracle.ts'; -import catalog from '../../benchmarks/fixtures/catalog.json' with { type: 'json' }; -import manifest from '../../benchmarks/fixtures/manifest.json' with { type: 'json' }; -import catalogText from '../../benchmarks/fixtures/catalog.json?raw'; +import { LockModel } from '../../src/lock.ts'; +import { PreparedSearch } from '../../src/lock.ts'; +import { SearchBudget } from '../../src/astar.ts'; +import { createSolverConfig } from '../../src/index.ts'; +import { BfsSearch } from '../../src/bfs.ts'; +import { referenceActions, replay } from './oracle.ts'; +import fixtures from '../benchmarks/fixtures.json' with { type: 'json' }; function position(value: number): Position { if (value === 1 || value === 2 || value === 3 || value === 4 || value === 5 || value === 6 || value === 7) return value; @@ -45,7 +45,7 @@ describe('independent minimum-action oracle', () => { const expected = referenceActions(lock); const prepared = new PreparedSearch(new LockModel(state, links)); const results = [solveLock(state, links), new BfsSearch(prepared, new SearchBudget()).solve(), - new BfsSearch(prepared, new SearchBudget({ maxDenseBytes: 0 })).solve()]; + new BfsSearch(prepared, new SearchBudget(createSolverConfig({ maxDenseBytes: 0 }))).solve()]; for (const result of results) { expect(result?.length ?? null, JSON.stringify(lock)).toBe(expected); if (result !== null) expect(replay(lock, result)).toEqual([4, 4, 4]); @@ -54,19 +54,19 @@ describe('independent minimum-action oracle', () => { }); }); -describe('preserved catalog', () => { - it('keeps all 45 fixtures and the pinned source hash', async () => { - const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(catalogText)); +describe('fixed mathematical expectations', () => { + it('keeps all 45 numerical configurations and action minima', async () => { + const semantics = fixtures.map(({ id, state, links, expectedActions }) => ({ id, state, links, expectedActions })); + const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify(semantics))); const hash = [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join(''); - expect(hash).toBe(manifest.catalogSha256); - expect(catalog).toHaveLength(45); - expect(new Set(catalog.map((entry) => entry.id)).size).toBe(45); - expect(manifest.source.revision).toBe('eee0bf50ebcb7fffd2b47954fd76bb015854e365'); + expect(hash).toBe('22cfb8a9610dd81a487dcecf40420474f9a634916af054622cbfe0ef9fafb4a4'); + expect(fixtures).toHaveLength(45); + expect(new Set(fixtures.map((entry) => entry.id)).size).toBe(45); }); - for (const entry of catalog) { + for (const entry of fixtures) { it(`${entry.id}: solves in ${entry.expectedActions} actions with legal intermediate moves`, () => { - const state = entry.definition.state.map(position); - const links = entry.definition.links.map((row) => row.map(link)); + const state = entry.state.map(position); + const links = entry.links.map((row) => row.map(link)); const before = { state: [...state], links: links.map((row) => [...row]) }; const result = solveLock(state, links); expect(result).toHaveLength(entry.expectedActions); diff --git a/tests/helpers/oracle.ts b/tests/unit/oracle.ts similarity index 100% rename from tests/helpers/oracle.ts rename to tests/unit/oracle.ts diff --git a/tests/unit/release-files.test.ts b/tests/unit/release-files.test.ts index f2b45a7..71f584d 100644 --- a/tests/unit/release-files.test.ts +++ b/tests/unit/release-files.test.ts @@ -3,8 +3,8 @@ import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { describe, expect, it } from 'vitest'; -import { prepareRelease, verifyBenchmarkEvidence, verifyPreparedRelease } from '../../scripts/release-files.ts'; -import { releaseAssets, sha256 } from '../../scripts/release-lib.ts'; +import { prepareRelease, verifyBenchmarkEvidence, verifyPreparedRelease } from '../../.github/scripts/release-files.ts'; +import { releaseAssets, sha256 } from '../../.github/scripts/release-lib.ts'; async function createPreparedFixture() { const root = await mkdtemp(join(tmpdir(), 'gothic-release-test-')); diff --git a/tests/unit/release-network.test.ts b/tests/unit/release-network.test.ts index 1c597f3..ea934c2 100644 --- a/tests/unit/release-network.test.ts +++ b/tests/unit/release-network.test.ts @@ -3,8 +3,8 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { releaseAssets, sha256, validateManifest } from '../../scripts/release-lib.ts'; -import { completeGithubRelease, publishOrResumeNpm, registryMetadata, verifyCdn, verifyRegistry } from '../../scripts/release-network.ts'; +import { releaseAssets, sha256, validateManifest } from '../../.github/scripts/release-lib.ts'; +import { completeGithubRelease, publishOrResumeNpm, registryMetadata, verifyCdn, verifyRegistry } from '../../.github/scripts/release-network.ts'; const command = vi.hoisted(() => vi.fn<(file: string, args: readonly string[]) => string>()); vi.mock('node:child_process', () => ({ execFileSync: command })); diff --git a/tests/unit/release.test.ts b/tests/unit/release.test.ts index e339fc5..96016c4 100644 --- a/tests/unit/release.test.ts +++ b/tests/unit/release.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { assertDigest, assertReleaseSource, canaryVersion, parseReleaseVersion, previousStableVersion, shouldPromoteGithubLatest, validateManifest, validateEvidenceGate } from '../../scripts/release-lib.ts'; +import { assertDigest, assertReleaseSource, canaryVersion, parseReleaseVersion, previousStableVersion, shouldPromoteGithubLatest, validateManifest, validateEvidenceGate } from '../../.github/scripts/release-lib.ts'; describe('release identity and publication guards', () => { it('accepts stable SemVer and rejects aliases, leading zeros, and shell text', () => { diff --git a/tsconfig.build.json b/tsconfig.build.json index b1eb91d..7acca1d 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -7,8 +7,15 @@ "rootDir": "src", "outDir": "dist", "types": [], - "lib": ["ES2022"] + "lib": [ + "ES2022" + ] }, - "include": ["src/**/*.ts"], - "exclude": ["node_modules", "dist"] + "include": [ + "src/index.ts" + ], + "exclude": [ + "node_modules", + "dist" + ] } diff --git a/tsconfig.json b/tsconfig.json index 2a315c3..d42c2ed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,11 @@ "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", - "lib": ["ES2022", "DOM", "DOM.Iterable"], + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable" + ], "strict": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, @@ -22,8 +26,20 @@ "noEmit": true, "allowImportingTsExtensions": true, "resolveJsonModule": true, - "types": ["node"] + "types": [ + "node" + ] }, - "include": ["src/**/*.ts", "cli/**/*.ts", "scripts/**/*.ts", "tests/**/*.ts", "benchmarks/**/*.ts", "*.config.ts"], - "exclude": ["node_modules", "dist", "tests/distribution/fixtures"] + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + ".github/scripts/**/*.ts", + "*.config.ts" + ], + "exclude": [ + "node_modules", + "dist", + "artifacts", + "tests/e2e/distribution/fixtures" + ] } diff --git a/vitest.config.ts b/vitest.config.ts index c28ee32..16addf8 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,14 +1,15 @@ import { playwright } from '@vitest/browser-playwright'; -import { defineConfig } from 'vitest/config'; +import type { TestUserConfig } from 'vitest/node'; -const shared = ['tests/unit/core*.test.ts', 'tests/browser/*.test.ts']; +const shared = ['tests/unit/core*.test.ts', 'tests/unit/config.test.ts', 'tests/e2e/browser/*.test.ts']; -export default defineConfig({ +// Plain Vite config keeps test options checked without globally augmenting Vite. +export default { test: { - coverage: { provider: 'v8', include: ['src/**/*.ts', 'cli/**/*.ts'], thresholds: { statements: 80, branches: 80, functions: 80, lines: 80 }, reporter: ['text', 'json-summary', 'html'] }, + coverage: { provider: 'v8', reportsDirectory: 'artifacts/coverage', include: ['src/**/*.ts'], thresholds: { statements: 80, branches: 80, functions: 80, lines: 80 }, reporter: ['text', 'json-summary', 'html'] }, projects: [ { test: { name: 'node', environment: 'node', include: [...shared, 'tests/unit/*.test.ts'], testTimeout: 120_000 } }, - { test: { name: 'distribution', environment: 'node', include: ['tests/distribution/*.test.ts'], testTimeout: 120_000 } }, + { test: { name: 'distribution', environment: 'node', include: ['tests/e2e/distribution/*.test.ts'], testTimeout: 120_000 } }, { test: { include: shared, @@ -22,5 +23,5 @@ export default defineConfig({ }, }, ], - }, -}); + } satisfies TestUserConfig, +}; diff --git a/docs/algorithm.md b/wiki/algorithm.md similarity index 91% rename from docs/algorithm.md rename to wiki/algorithm.md index 3fb8ec3..4418f70 100644 --- a/docs/algorithm.md +++ b/wiki/algorithm.md @@ -25,10 +25,8 @@ be combined, and opposite movements can be cancelled. The combined displacement still lies within the plate's six-position travel range. Thus a nonempty shortest solution has A−1 switches, although it may revisit a plate later and have U Date: Mon, 7 Sep 2026 17:55:24 +0400 Subject: [PATCH 9/9] refactor: clarify solver operations and consolidate delivery workflow --- .github/scripts/build.ts | 93 ++++-- .github/scripts/ci-baseline.ts | 156 ++++++--- .github/scripts/release-browser.ts | 141 ++++++--- .github/scripts/release-files.ts | 224 ++++++++++--- .github/scripts/release-lib.ts | 214 ++++++++++--- .github/scripts/release-network.ts | 297 +++++++++++++----- .github/scripts/release.ts | 267 +++++++++++----- .github/workflows/ci.yml | 179 ----------- .github/workflows/release.yml | 292 +++++++---------- .oxlintrc.json | 14 + AGENTS.md | 8 + .../plans/2026-09-07-simplify-library.md | 219 ++++--------- src/astar.ts | 289 ++++++++++------- src/bfs.ts | 221 ++++++++----- src/cli.ts | 111 +++++-- src/config.ts | 56 +++- src/errors.ts | 15 +- src/index.ts | 11 +- src/lock.ts | 237 ++++++++------ src/matrix.ts | 186 +++++++---- src/priority-queue.ts | 98 ++++-- src/types.ts | 8 + tests/unit/core-limits.test.ts | 2 +- tests/unit/core-oracle.test.ts | 2 +- tests/unit/release-network.test.ts | 27 +- wiki/development.md | 6 + wiki/releasing.md | 20 +- 27 files changed, 2120 insertions(+), 1273 deletions(-) delete mode 100644 .github/workflows/ci.yml diff --git a/.github/scripts/build.ts b/.github/scripts/build.ts index 37c61a3..6f5973e 100644 --- a/.github/scripts/build.ts +++ b/.github/scripts/build.ts @@ -7,25 +7,82 @@ import { build } from 'vite'; const root = fileURLToPath(new URL('../../', import.meta.url)); const dist = new URL('../../dist/', import.meta.url); + +async function buildCli(): Promise { + await build({ + root, + configFile: false, + build: { + target: 'node22', + outDir: 'dist', + emptyOutDir: false, + minify: false, + sourcemap: false, + lib: { + entry: fileURLToPath(new URL('../../src/cli.ts', import.meta.url)), + formats: ['es'], + fileName: () => 'gothic-lock-solver.cli.mjs', + }, + rolldownOptions: { + external: [...builtinModules, ...builtinModules.map((name) => `node:${name}`)], + output: { + codeSplitting: false, + comments: { legal: false }, + }, + }, + }, + }); +} + +async function buildDeclarations(): Promise { + const compiler = fileURLToPath(new URL('../../node_modules/typescript/bin/tsc', import.meta.url)); + execFileSync(process.execPath, [compiler, '-p', 'tsconfig.build.json'], { + cwd: root, + stdio: 'inherit', + }); + + const globalDeclaration = [ + '/** Types for the classic GothicLockSolver global. */', + 'export as namespace GothicLockSolver;', + "export * from './index.js';", + '', + ].join('\n'); + await writeFile(new URL('global.d.ts', dist), globalDeclaration); +} + +async function writeBuildManifest(): Promise { + const files: Record = {}; + for (const filename of (await readdir(dist)).sort()) { + const bytes = await readFile(new URL(filename, dist)); + files[filename] = createHash('sha256').update(bytes).digest('hex'); + } + + const metadata: unknown = JSON.parse(await readFile(new URL('../../package.json', import.meta.url), 'utf8')); + if (typeof metadata !== 'object' || metadata === null || !('version' in metadata) || typeof metadata.version !== 'string') { + throw new Error('Package version is missing.'); + } + const sourceSha = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim(); + const manifest = { + schemaVersion: 1, + package: 'gothic-lock-solver', + version: metadata.version, + sourceSha, + files, + }; + await writeFile(new URL('manifest.json', dist), `${JSON.stringify(manifest, null, 2)}\n`); +} + await rm(dist, { recursive: true, force: true }); await mkdir(dist, { recursive: true }); + +// Each library pass produces ESM and classic-script files; the CLI is a separate entry. for (const mode of ['readable', 'minified']) { - await build({ root, configFile: fileURLToPath(new URL('../../vite.config.ts', import.meta.url)), mode }); -} -await build({ - root, configFile: false, build: { - target: 'node22', outDir: 'dist', emptyOutDir: false, minify: false, sourcemap: false, - lib: { entry: fileURLToPath(new URL('../../src/cli.ts', import.meta.url)), formats: ['es'], fileName: () => 'gothic-lock-solver.cli.mjs' }, - rolldownOptions: { external: [...builtinModules, ...builtinModules.map((name) => `node:${name}`)], output: { codeSplitting: false, comments: { legal: false } } }, - }, -}); -execFileSync(process.execPath, [fileURLToPath(new URL('../../node_modules/typescript/bin/tsc', import.meta.url)), '-p', 'tsconfig.build.json'], { cwd: root, stdio: 'inherit' }); -await writeFile(new URL('global.d.ts', dist), `/** Types for the classic GothicLockSolver global. */\nexport as namespace GothicLockSolver;\nexport * from './index.js';\n`); -const files: Record = {}; -for (const filename of (await readdir(dist)).sort()) { - files[filename] = createHash('sha256').update(await readFile(new URL(filename, dist))).digest('hex'); + await build({ + root, + configFile: fileURLToPath(new URL('../../vite.config.ts', import.meta.url)), + mode, + }); } -const metadata: unknown = JSON.parse(await readFile(new URL('../../package.json', import.meta.url), 'utf8')); -if (typeof metadata !== 'object' || metadata === null || !('version' in metadata) || typeof metadata.version !== 'string') throw new Error('Package version is missing.'); -const sourceSha = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim(); -await writeFile(new URL('manifest.json', dist), `${JSON.stringify({ schemaVersion: 1, package: 'gothic-lock-solver', version: metadata.version, sourceSha, files }, null, 2)}\n`); +await buildCli(); +await buildDeclarations(); +await writeBuildManifest(); diff --git a/.github/scripts/ci-baseline.ts b/.github/scripts/ci-baseline.ts index 8673b31..a4b8fe8 100644 --- a/.github/scripts/ci-baseline.ts +++ b/.github/scripts/ci-baseline.ts @@ -1,67 +1,129 @@ import { execFileSync } from 'node:child_process'; import { appendFile, mkdir, readFile, stat, writeFile } from 'node:fs/promises'; import { join, resolve } from 'node:path'; -import { assertDigest, previousStableVersion, record, sha256, string, validateManifest } from './release-lib.ts'; +import { + NPM_REGISTRY, + PACKAGE_NAME, + assertDigest, + previousStableVersion, + record, + sha256, + string, + validateManifest, +} from './release-lib.ts'; import { writeJson } from './release-files.ts'; import { fetchBytes, verifyRegistry } from './release-network.ts'; -const [mode, directoryArgument, outputArgument] = process.argv.slice(2); -if (!['checkout', 'release'].includes(mode ?? '') || !directoryArgument || !outputArgument) throw new Error('Usage: node .github/scripts/ci-baseline.ts checkout BASE_CHECKOUT OUTPUT_DIR | release CANDIDATE_VERSION OUTPUT_DIR'); -const output = resolve(outputArgument); -await mkdir(output, { recursive: true }); +const REGISTRY_TIMEOUT_MS = 30_000; async function emit(fields: Readonly>): Promise { - const text = Object.entries(fields).map(([key, value]) => `${key}=${value}\n`).join(''); - if (process.env['GITHUB_OUTPUT']) await appendFile(process.env['GITHUB_OUTPUT'], text); + const text = Object.entries(fields) + .map(([key, value]) => `${key}=${value}\n`) + .join(''); + const githubOutput = process.env['GITHUB_OUTPUT']; + if (githubOutput) { + await appendFile(githubOutput, text); + } console.log(JSON.stringify(fields)); } -if (mode === 'checkout') { - const base = resolve(directoryArgument); +async function checkoutBaseline(checkoutDirectory: string, outputDirectory: string): Promise { + const base = resolve(checkoutDirectory); const legacyModule = join(base, 'src/index.mjs'); - const legacy = await stat(legacyModule).then(value => value.isFile(), () => false); + const legacy = await stat(legacyModule).then( + (value) => value.isFile(), + () => false, + ); const module = legacy ? legacyModule : join(base, 'dist/gothic-lock-solver.mjs'); const format = legacy ? 'legacy' : 'tuple'; - const snapshot = join(output, 'baseline-snapshot.json'); + const snapshot = join(outputDirectory, 'baseline-snapshot.json'); + // The supplied base checkout, never a candidate-owned expected snapshot, supplies the live baseline. - execFileSync(process.execPath, ['tests/benchmarks/snapshot.ts', '--source-module', module, '--source-format', format, - '--reason', 'Fresh snapshot from the separately checked-out PR target/reference commit; candidate snapshot is not read.', '--output', snapshot], { stdio: 'inherit' }); + execFileSync(process.execPath, [ + 'tests/benchmarks/snapshot.ts', + '--source-module', module, + '--source-format', format, + '--reason', 'Fresh snapshot from the separately checked-out PR target/reference commit; candidate snapshot is not read.', + '--output', snapshot, + ], { stdio: 'inherit' }); await emit({ available: 'true', module, snapshot, format }); -} else { - const response = await fetch('https://registry.npmjs.org/gothic-lock-solver', { signal: AbortSignal.timeout(30_000) }); +} + +async function releaseBaseline(candidateVersion: string, outputDirectory: string): Promise { + const response = await fetch(`${NPM_REGISTRY}/${PACKAGE_NAME}`, { + signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS), + }); if (response.status === 404) { await emit({ available: 'false', reason: 'No npm package yet; use the explicitly pinned reference checkout.' }); - } else { - if (!response.ok) throw new Error(`npm baseline metadata HTTP ${response.status}`); - const metadata: unknown = await response.json(); - const versions = record(record(metadata, 'package metadata')['versions'], 'versions'); - const previous = previousStableVersion(Object.keys(versions), directoryArgument); - if (previous === null) { - await emit({ available: 'false', reason: 'No preceding stable version; use the explicitly pinned reference checkout.' }); - } else { - const prefix = `https://github.com/Marcus-Rise/gothic-lock-solver/releases/download/v${previous}/`; - const manifestBytes = await fetchBytes(`${prefix}release-manifest.json`); - const manifestJson: unknown = JSON.parse(manifestBytes.toString('utf8')); - const manifest = validateManifest(manifestJson); - if (manifest.version !== previous || manifest.kind !== 'stable') throw new Error('Previous stable release identity mismatch'); - await verifyRegistry(manifest); - const moduleBytes = await fetchBytes(`${prefix}gothic-lock-solver.mjs`); - assertDigest(moduleBytes, string(manifest.assets['gothic-lock-solver.mjs'], 'previous ESM hash'), 'previous release module'); - const evidenceBytes = await fetchBytes(`${prefix}benchmark-evidence.json`); - assertDigest(evidenceBytes, string(manifest.assets['benchmark-evidence.json'], 'previous evidence hash'), 'previous release evidence'); - const evidenceJson: unknown = JSON.parse(evidenceBytes.toString('utf8')); - const evidence = record(evidenceJson, 'previous evidence'); - const snapshot = record(evidence['qualitySnapshot'], 'previous quality snapshot'); - const source = record(snapshot['source'], 'previous snapshot source'); - if (source['revision'] !== manifest.sourceSha || source['moduleSha256'] !== sha256(moduleBytes)) throw new Error('Previous release snapshot source differs from published module'); - const module = join(output, 'baseline.mjs'); const snapshotPath = join(output, 'baseline-snapshot.json'); - await writeFile(module, moduleBytes, { flag: 'wx' }); - await writeFile(join(output, 'previous-release-manifest.json'), manifestBytes, { flag: 'wx' }); - await writeFile(join(output, 'previous-benchmark-evidence.json'), evidenceBytes, { flag: 'wx' }); - await writeJson(snapshotPath, { ...snapshot, source: { ...source, releaseVersion: previous, kind: 'previous-stable-release', releaseManifestSha256: sha256(manifestBytes), releaseEvidenceSha256: sha256(evidenceBytes) } }); - // Re-read the bytes that will actually be imported, after writing the evidence chain. - assertDigest(await readFile(module), string(source['moduleSha256'], 'snapshot module hash'), 'saved baseline module'); - await emit({ available: 'true', module, snapshot: snapshotPath, format: 'tuple', version: previous }); - } + return; } + if (!response.ok) { + throw new Error(`npm baseline metadata HTTP ${response.status}`); + } + + const metadata: unknown = await response.json(); + const versions = record(record(metadata, 'package metadata')['versions'], 'versions'); + const previous = previousStableVersion(Object.keys(versions), candidateVersion); + if (previous === null) { + await emit({ available: 'false', reason: 'No preceding stable version; use the explicitly pinned reference checkout.' }); + return; + } + + const prefix = `https://github.com/Marcus-Rise/gothic-lock-solver/releases/download/v${previous}/`; + const manifestBytes = await fetchBytes(`${prefix}release-manifest.json`); + const manifestJson: unknown = JSON.parse(manifestBytes.toString('utf8')); + const manifest = validateManifest(manifestJson); + if (manifest.version !== previous || manifest.kind !== 'stable') { + throw new Error('Previous stable release identity mismatch'); + } + await verifyRegistry(manifest); + + const moduleBytes = await fetchBytes(`${prefix}gothic-lock-solver.mjs`); + const moduleHash = string(manifest.assets['gothic-lock-solver.mjs'], 'previous ESM hash'); + assertDigest(moduleBytes, moduleHash, 'previous release module'); + + const evidenceBytes = await fetchBytes(`${prefix}benchmark-evidence.json`); + const evidenceHash = string(manifest.assets['benchmark-evidence.json'], 'previous evidence hash'); + assertDigest(evidenceBytes, evidenceHash, 'previous release evidence'); + + const evidenceJson: unknown = JSON.parse(evidenceBytes.toString('utf8')); + const evidence = record(evidenceJson, 'previous evidence'); + const snapshot = record(evidence['qualitySnapshot'], 'previous quality snapshot'); + const source = record(snapshot['source'], 'previous snapshot source'); + if (source['revision'] !== manifest.sourceSha || source['moduleSha256'] !== sha256(moduleBytes)) { + throw new Error('Previous release snapshot source differs from published module'); + } + + const module = join(outputDirectory, 'baseline.mjs'); + const snapshotPath = join(outputDirectory, 'baseline-snapshot.json'); + await writeFile(module, moduleBytes, { flag: 'wx' }); + await writeFile(join(outputDirectory, 'previous-release-manifest.json'), manifestBytes, { flag: 'wx' }); + await writeFile(join(outputDirectory, 'previous-benchmark-evidence.json'), evidenceBytes, { flag: 'wx' }); + await writeJson(snapshotPath, { + ...snapshot, + source: { + ...source, + releaseVersion: previous, + kind: 'previous-stable-release', + releaseManifestSha256: sha256(manifestBytes), + releaseEvidenceSha256: sha256(evidenceBytes), + }, + }); + + // Re-read the bytes that will actually be imported, after writing the evidence chain. + assertDigest(await readFile(module), string(source['moduleSha256'], 'snapshot module hash'), 'saved baseline module'); + await emit({ available: 'true', module, snapshot: snapshotPath, format: 'tuple', version: previous }); +} + +const [mode, sourceArgument, outputArgument] = process.argv.slice(2); +if (!['checkout', 'release'].includes(mode ?? '') || !sourceArgument || !outputArgument) { + throw new Error('Usage: node .github/scripts/ci-baseline.ts checkout BASE_CHECKOUT OUTPUT_DIR | release CANDIDATE_VERSION OUTPUT_DIR'); +} + +const outputDirectory = resolve(outputArgument); +await mkdir(outputDirectory, { recursive: true }); +if (mode === 'checkout') { + await checkoutBaseline(sourceArgument, outputDirectory); +} else { + await releaseBaseline(sourceArgument, outputDirectory); } diff --git a/.github/scripts/release-browser.ts b/.github/scripts/release-browser.ts index 874522c..9de9417 100644 --- a/.github/scripts/release-browser.ts +++ b/.github/scripts/release-browser.ts @@ -1,8 +1,94 @@ import { createServer } from 'node:http'; -import { chromium, firefox, webkit } from 'playwright'; -import { loadCatalog } from '../../tests/benchmarks/catalog.ts'; +import { chromium, firefox, webkit, type Browser, type BrowserType } from 'playwright'; +import { loadCatalog, type Fixture } from '../../tests/benchmarks/catalog.ts'; import { replayCommands, tupleCommands } from '../../tests/benchmarks/validation.ts'; -import { runtimeFiles, type ReleaseManifest } from './release-lib.ts'; +import { CDN_ORIGIN, REQUIRED_LOCK_COUNT, runtimeFiles, type ReleaseManifest } from './release-lib.ts'; + +const TARGET_POSITION = 4; + +function verifySolutions(results: unknown, locks: readonly Fixture[], context: string): void { + if (!Array.isArray(results) || results.length !== REQUIRED_LOCK_COUNT) { + throw new Error('CDN fixture results are incomplete'); + } + + for (const [index, result] of results.entries()) { + const lock = locks[index]; + if (!lock) { + throw new Error('Unexpected CDN fixture'); + } + const commands = tupleCommands(result); + const hasMinimumActions = commands.length === lock.expectedActions; + const solved = hasMinimumActions && replayCommands(lock, commands) + .every((position) => position === TARGET_POSITION); + if (!solved) { + throw new Error(`CDN fixture mismatch: ${context}/${lock.id}`); + } + } +} + +async function verifyBundle( + browser: Browser, + filename: string, + manifest: ReleaseManifest, + locks: readonly Fixture[], + origin: string, +): Promise { + const engineName = browser.browserType().name(); + const page = await browser.newPage(); + try { + await page.goto(origin); + const url = `${CDN_ORIGIN}/npm/${manifest.name}@${manifest.version}/dist/${filename}`; + const runtimeRequests: string[] = []; + page.on('request', (request) => { + if (request.url().startsWith('https://')) { + runtimeRequests.push(request.url()); + } + }); + + if (filename.endsWith('.js')) { + await page.addScriptTag({ url }); + } + const results: unknown = await page.evaluate(async ({ moduleUrl, moduleFormat, definitions }) => { + const namespace: unknown = moduleFormat + ? await import(moduleUrl) + : Reflect.get(globalThis, 'GothicLockSolver'); + if (typeof namespace !== 'object' || namespace === null || !('solveLock' in namespace) || typeof namespace.solveLock !== 'function') { + throw new Error('Missing CDN solveLock export'); + } + const solve = namespace.solveLock; + return definitions.map((definition): unknown => Reflect.apply(solve, undefined, [definition.state, definition.links])); + }, { + moduleUrl: url, + moduleFormat: filename.endsWith('.mjs'), + definitions: locks.map(({ state, links }) => ({ state, links })), + }); + + verifySolutions(results, locks, `${engineName}/${filename}`); + // Each bundle must work with exactly its own request and no runtime imports. + if (runtimeRequests.length !== 1 || runtimeRequests[0] !== url) { + throw new Error(`Additional CDN runtime dependency: ${runtimeRequests.join(', ')}`); + } + console.log(`CDN browser passed: ${engineName}, ${filename}, ${REQUIRED_LOCK_COUNT} fixtures`); + } finally { + await page.close(); + } +} + +async function verifyEngine( + engine: BrowserType, + manifest: ReleaseManifest, + locks: readonly Fixture[], + origin: string, +): Promise { + const browser = await engine.launch(); + try { + for (const filename of runtimeFiles) { + await verifyBundle(browser, filename, manifest, locks, origin); + } + } finally { + await browser.close(); + } +} /** Real CDN imports, in each supported engine, after byte/header verification. */ export async function verifyCdnBrowsers(manifest: ReleaseManifest): Promise { @@ -11,39 +97,24 @@ export async function verifyCdnBrowsers(manifest: ReleaseManifest): PromiseGothic Lock Solver CDN verification'); }); - await new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', resolve); }); - const address = server.address(); - if (address === null || typeof address === 'string') throw new Error('Local CDN test origin unavailable'); + await new Promise((resolve, reject) => { + server.once('error', reject); + server.listen(0, '127.0.0.1', resolve); + }); + try { + const address = server.address(); + if (address === null || typeof address === 'string') { + throw new Error('Local CDN test origin unavailable'); + } + const origin = `http://127.0.0.1:${address.port}/`; for (const engine of [chromium, firefox, webkit]) { - const browser = await engine.launch(); - try { - for (const filename of runtimeFiles) { - const page = await browser.newPage(); - try { - await page.goto(`http://127.0.0.1:${address.port}/`); - const url = `https://cdn.jsdelivr.net/npm/${manifest.name}@${manifest.version}/dist/${filename}`; - const runtimeRequests: string[] = []; - page.on('request', request => { if (request.url().startsWith('https://')) runtimeRequests.push(request.url()); }); - if (filename.endsWith('.js')) await page.addScriptTag({ url }); - const results: unknown = await page.evaluate(async ({ moduleUrl, moduleFormat, definitions }) => { - const namespace: unknown = moduleFormat ? await import(moduleUrl) : Reflect.get(globalThis, 'GothicLockSolver'); - if (typeof namespace !== 'object' || namespace === null || !('solveLock' in namespace) || typeof namespace.solveLock !== 'function') throw new Error('Missing CDN solveLock export'); - const solve = namespace.solveLock; - return definitions.map((definition): unknown => Reflect.apply(solve, undefined, [definition.state, definition.links])); - }, { moduleUrl: url, moduleFormat: filename.endsWith('.mjs'), definitions: locks.map(({ state, links }) => ({ state, links })) }); - if (!Array.isArray(results) || results.length !== 45) throw new Error('CDN fixture results are incomplete'); - for (const [index, result] of results.entries()) { - const lock = locks[index]; - if (!lock) throw new Error('Unexpected CDN fixture'); - const commands = tupleCommands(result); - if (commands.length !== lock.expectedActions || replayCommands(lock, commands).some(position => position !== 4)) throw new Error(`CDN fixture mismatch: ${engine.name()}/${filename}/${lock.id}`); - } - if (runtimeRequests.length !== 1 || runtimeRequests[0] !== url) throw new Error(`Additional CDN runtime dependency: ${runtimeRequests.join(', ')}`); - console.log(`CDN browser passed: ${engine.name()}, ${filename}, 45 fixtures`); - } finally { await page.close(); } - } - } finally { await browser.close(); } + await verifyEngine(engine, manifest, locks, origin); } - } finally { await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve())); } + } finally { + await new Promise((resolve, reject) => server.close((error) => { + if (error) reject(error); + else resolve(); + })); + } } diff --git a/.github/scripts/release-files.ts b/.github/scripts/release-files.ts index 8c7165e..94b9771 100644 --- a/.github/scripts/release-files.ts +++ b/.github/scripts/release-files.ts @@ -2,7 +2,24 @@ import { execFileSync } from 'node:child_process'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { join, resolve } from 'node:path'; import { isDeepStrictEqual } from 'node:util'; -import { assertDigest, parseReleaseVersion, record, releaseAssets, sha256, string, validateManifest, validateEvidenceGate, type ReleaseKind, type ReleaseManifest } from './release-lib.ts'; +import { + CDN_ORIGIN, + PACKAGE_NAME, + REQUIRED_LOCK_COUNT, + assertDigest, + executableFiles, + parseReleaseVersion, + record, + releaseAssets, + sha256, + string, + validateEvidenceGate, + validateManifest, + type ReleaseKind, + type ReleaseManifest, +} from './release-lib.ts'; + +const MAX_ARCHIVE_FILE_BYTES = 16 * 1024 * 1024; export async function readJson(path: string): Promise { const value: unknown = JSON.parse(await readFile(path, 'utf8')); @@ -14,7 +31,9 @@ export async function writeJson(path: string, value: unknown): Promise { } function archiveFile(tarball: string, filename: string): Buffer { - return execFileSync('tar', ['-xOf', tarball, `package/${filename}`], { maxBuffer: 16 * 1024 * 1024 }); + return execFileSync('tar', ['-xOf', tarball, `package/${filename}`], { + maxBuffer: MAX_ARCHIVE_FILE_BYTES, + }); } export interface EvidenceAcceptance { @@ -22,22 +41,49 @@ export interface EvidenceAcceptance { readonly acceptanceReason?: string; } -export async function verifyPreparedRelease(directory: string, acceptance: EvidenceAcceptance = {}): Promise { +export async function verifyPreparedRelease( + directory: string, + acceptance: EvidenceAcceptance = {}, +): Promise { const manifest = validateManifest(await readJson(join(directory, 'release-manifest.json'))); - for (const filename of releaseAssets) assertDigest(await readFile(join(directory, filename)), string(manifest.assets[filename], filename), filename); + for (const filename of releaseAssets) { + const bytes = await readFile(join(directory, filename)); + const expectedHash = string(manifest.assets[filename], filename); + assertDigest(bytes, expectedHash, filename); + } + const tarball = resolve(directory, 'package.tgz'); const packed: unknown = JSON.parse(archiveFile(tarball, 'package.json').toString('utf8')); const metadata = record(packed, 'packed metadata'); - if (metadata['name'] !== manifest.name || metadata['version'] !== manifest.version || metadata['private'] === true) throw new Error('Packed package identity differs from release'); + if ( + metadata['name'] !== manifest.name || + metadata['version'] !== manifest.version || + metadata['private'] === true + ) { + throw new Error('Packed package identity differs from release'); + } + const build: unknown = JSON.parse(archiveFile(tarball, 'dist/manifest.json').toString('utf8')); const buildManifest = record(build, 'packed build manifest'); - if (buildManifest['sourceSha'] !== manifest.sourceSha || buildManifest['version'] !== manifest.version) throw new Error('Packed build source/version differs from release'); - for (const filename of releaseAssets.filter(name => /\.(?:mjs|js)$/.test(name))) { - assertDigest(archiveFile(tarball, `dist/${filename}`), string(manifest.assets[filename], filename), `packed ${filename}`); + if (buildManifest['sourceSha'] !== manifest.sourceSha || buildManifest['version'] !== manifest.version) { + throw new Error('Packed build source/version differs from release'); + } + + for (const filename of executableFiles) { + const bytes = archiveFile(tarball, `dist/${filename}`); + const expectedHash = string(manifest.assets[filename], filename); + assertDigest(bytes, expectedHash, `packed ${filename}`); } + // The publishing-side verifier receives acceptance from its trusted caller, never from an artifact. - await verifyBenchmarkEvidence(directory, manifest.sourceSha, string(manifest.assets['gothic-lock-solver.mjs'], 'prepared ESM hash'), - acceptance.acceptedEvidenceSha256, acceptance.acceptanceReason); + const moduleSha256 = string(manifest.assets['gothic-lock-solver.mjs'], 'prepared ESM hash'); + await verifyBenchmarkEvidence( + directory, + manifest.sourceSha, + moduleSha256, + acceptance.acceptedEvidenceSha256, + acceptance.acceptanceReason, + ); return manifest; } @@ -51,68 +97,158 @@ export interface PrepareOptions { readonly acceptanceReason?: string; } -export async function verifyBenchmarkEvidence(directory: string, sourceSha: string, moduleSha256: string, acceptedSha256 = '', acceptanceReason = ''): Promise { +export async function verifyBenchmarkEvidence( + directory: string, + sourceSha: string, + moduleSha256: string, + acceptedSha256 = '', + acceptanceReason = '', +): Promise { const bytes = await readFile(join(directory, 'benchmark-evidence.json')); - const evidence = record(await readJson(join(directory, 'benchmark-evidence.json')), 'benchmark evidence'); + const parsed: unknown = JSON.parse(bytes.toString('utf8')); + const evidence = record(parsed, 'benchmark evidence'); const acceptance = validateEvidenceGate(evidence, sha256(bytes), acceptedSha256, acceptanceReason); const metadata = record(evidence['metadata'], 'benchmark metadata'); const candidate = record(metadata['candidate'], 'benchmark candidate'); - if (candidate['revision'] !== sourceSha || candidate['moduleSha256'] !== moduleSha256 || candidate['format'] !== 'tuple') throw new Error('Benchmark candidate differs from prepared source/artifact'); + if ( + candidate['revision'] !== sourceSha || + candidate['moduleSha256'] !== moduleSha256 || + candidate['format'] !== 'tuple' + ) { + throw new Error('Benchmark candidate differs from prepared source/artifact'); + } + const reports = record(evidence['reports'], 'benchmark report hashes'); - for (const [field, filename] of [['json', 'benchmark.json'], ['markdown', 'benchmark.md']]) { - if (!field || !filename) throw new Error('Missing report path'); + const reportFiles = [ + ['json', 'benchmark.json'], + ['markdown', 'benchmark.md'], + ] as const; + for (const [field, filename] of reportFiles) { const report = record(reports[field], field); - if (report['path'] !== filename) throw new Error('Unexpected benchmark report path'); - assertDigest(await readFile(join(directory, filename)), string(report['sha256'], 'report hash'), filename); + if (report['path'] !== filename) { + throw new Error('Unexpected benchmark report path'); + } + const reportBytes = await readFile(join(directory, filename)); + const expectedHash = string(report['sha256'], 'report hash'); + assertDigest(reportBytes, expectedHash, filename); } + const report = record(await readJson(join(directory, 'benchmark.json')), 'detailed benchmark report'); - if (record(report['correctness'], 'report correctness')['passed'] !== true) throw new Error('Detailed report correctness must pass'); + const correctness = record(report['correctness'], 'report correctness'); + if (correctness['passed'] !== true) { + throw new Error('Detailed report correctness must pass'); + } for (const field of ['schemaVersion', 'quality', 'performance', 'config', 'metadata', 'memory']) { - if (!isDeepStrictEqual(report[field], evidence[field])) throw new Error(`Detailed report ${field} differs from benchmark evidence`); + if (!isDeepStrictEqual(report[field], evidence[field])) { + throw new Error(`Detailed report ${field} differs from benchmark evidence`); + } } return acceptance; } +function releaseAssetPath(filename: string, options: PrepareOptions): string { + if (filename === 'package.tgz') { + return join(options.root, 'artifacts/package/package.tgz'); + } + if (filename.startsWith('benchmark')) { + return join(options.benchmarkDir, filename); + } + return join(options.root, 'dist', filename); +} + /** Copies the already tested archive; this function never invokes a build or npm pack. */ export async function prepareRelease(options: PrepareOptions): Promise { const build = record(await readJson(join(options.root, 'dist/manifest.json')), 'build manifest'); const metadata = record(await readJson(join(options.root, 'package.json')), 'package metadata'); const version = string(metadata['version'], 'package version'); - if (parseReleaseVersion(version).prerelease !== (options.kind === 'canary')) throw new Error('Stable/canary version mismatch'); - if (build['sourceSha'] !== options.sourceSha || build['version'] !== version) throw new Error('Build source/version differs from requested release'); + if (parseReleaseVersion(version).prerelease !== (options.kind === 'canary')) { + throw new Error('Stable/canary version mismatch'); + } + if (build['sourceSha'] !== options.sourceSha || build['version'] !== version) { + throw new Error('Build source/version differs from requested release'); + } + const moduleSha256 = sha256(await readFile(join(options.root, 'dist/gothic-lock-solver.mjs'))); - const acceptance = await verifyBenchmarkEvidence(options.benchmarkDir, options.sourceSha, moduleSha256, options.acceptedEvidenceSha256, options.acceptanceReason); + const acceptance = await verifyBenchmarkEvidence( + options.benchmarkDir, + options.sourceSha, + moduleSha256, + options.acceptedEvidenceSha256, + options.acceptanceReason, + ); + const assets: Record = {}; await mkdir(options.outputDir, { recursive: true }); for (const filename of releaseAssets) { - const from = filename === 'package.tgz' ? join(options.root, 'artifacts/package/package.tgz') - : filename.startsWith('benchmark') ? join(options.benchmarkDir, filename) - : join(options.root, 'dist', filename); - const bytes = await readFile(from); + const bytes = await readFile(releaseAssetPath(filename, options)); assets[filename] = sha256(bytes); await writeFile(join(options.outputDir, filename), bytes, { flag: 'wx' }); } - const manifest = validateManifest({ schemaVersion: 1, name: 'gothic-lock-solver', version, sourceSha: options.sourceSha, kind: options.kind, - tarballSha256: assets['package.tgz'], assets }); + + const manifest = validateManifest({ + schemaVersion: 1, + name: PACKAGE_NAME, + version, + sourceSha: options.sourceSha, + kind: options.kind, + tarballSha256: assets['package.tgz'], + assets, + }); await writeJson(join(options.outputDir, 'release-manifest.json'), manifest); await verifyPreparedRelease(options.outputDir, options); + const evidence = record(await readJson(join(options.benchmarkDir, 'benchmark-evidence.json')), 'release evidence'); - const evidenceMetadata = record(evidence['metadata'], 'evidence metadata'); - const baseline = record(evidenceMetadata['baseline'] ?? {}, 'evidence baseline'); - const memoryPerformance = record(record(evidence['memory'], 'memory evidence')['performance'], 'memory verdict'); - const cdn = `https://cdn.jsdelivr.net/npm/${manifest.name}@${manifest.version}/dist`; - const notes = [ - `# Gothic Lock Solver ${manifest.version}`, '', `Source: ${manifest.sourceSha}.`, '', - `[npm ${manifest.version}](https://www.npmjs.com/package/${manifest.name}/v/${manifest.version})`, '', - `Install: \`npm install ${manifest.name}@${manifest.version}\`.`, '', - 'This release provides solveLock(state, links, config?) and createSolverConfig(overrides?). Commands are [zero-based index, signed numeric pin delta]. See the Wiki API contract for integration details.', '', - `Benchmark quality: **passed**, 45 fixtures. Performance: **${string(evidence['performance'], 'performance')}**; memory: **${string(memoryPerformance['verdict'], 'memory verdict')}**.`, - `Baseline: ${typeof baseline['kind'] === 'string' ? baseline['kind'] : 'reference/previous release'}, source ${typeof baseline['revision'] === 'string' ? baseline['revision'] : 'see benchmark-evidence.json'}. Detailed report hashes and the comparison source are preserved in benchmark-evidence.json.`, - acceptance === null ? 'The calibrated timing and memory gates passed.' : `Accepted performance evidence: ${acceptance}`, '', - '```html', ``, '', '```', '', - '```html', '', '```', '', - 'All five executable files, the exact tested npm archive, checksum manifest and detailed benchmark reports are attached.', '', - ].join('\n'); + const notes = releaseNotes(manifest, evidence, acceptance); await writeFile(join(options.outputDir, 'release-notes.md'), notes, { flag: 'wx' }); return manifest; } + +function releaseNotes( + manifest: ReleaseManifest, + evidence: Record, + acceptance: string | null, +): string { + const evidenceMetadata = record(evidence['metadata'], 'evidence metadata'); + const baseline = record(evidenceMetadata['baseline'] ?? {}, 'evidence baseline'); + const baselineKind = typeof baseline['kind'] === 'string' ? baseline['kind'] : 'reference/previous release'; + const baselineRevision = typeof baseline['revision'] === 'string' + ? baseline['revision'] + : 'see benchmark-evidence.json'; + const memory = record(evidence['memory'], 'memory evidence'); + const memoryPerformance = record(memory['performance'], 'memory verdict'); + const timingVerdict = string(evidence['performance'], 'performance'); + const memoryVerdict = string(memoryPerformance['verdict'], 'memory verdict'); + const acceptanceNote = acceptance === null + ? 'The calibrated timing and memory gates passed.' + : `Accepted performance evidence: ${acceptance}`; + const cdn = `${CDN_ORIGIN}/npm/${manifest.name}@${manifest.version}/dist`; + + return `# Gothic Lock Solver ${manifest.version} + +Source: ${manifest.sourceSha}. + +[npm ${manifest.version}](https://www.npmjs.com/package/${manifest.name}/v/${manifest.version}) + +Install: \`npm install ${manifest.name}@${manifest.version}\`. + +This release provides solveLock(state, links, config?) and createSolverConfig(overrides?). Commands are [zero-based index, signed numeric pin delta]. See the Wiki API contract for integration details. + +Benchmark quality: **passed**, ${REQUIRED_LOCK_COUNT} fixtures. Performance: **${timingVerdict}**; memory: **${memoryVerdict}**. +Baseline: ${baselineKind}, source ${baselineRevision}. Detailed report hashes and the comparison source are preserved in benchmark-evidence.json. +${acceptanceNote} + +\`\`\`html + + +\`\`\` + +\`\`\`html + +\`\`\` + +All five executable files, the exact tested npm archive, checksum manifest and detailed benchmark reports are attached. +`; +} diff --git a/.github/scripts/release-lib.ts b/.github/scripts/release-lib.ts index 1a1dd37..b4ed8bf 100644 --- a/.github/scripts/release-lib.ts +++ b/.github/scripts/release-lib.ts @@ -1,12 +1,37 @@ import { createHash } from 'node:crypto'; -export const runtimeFiles = ['gothic-lock-solver.mjs', 'gothic-lock-solver.min.mjs', 'gothic-lock-solver.js', 'gothic-lock-solver.min.js'] as const; -export const releaseAssets = [...runtimeFiles, 'gothic-lock-solver.cli.mjs', 'benchmark-evidence.json', 'benchmark.json', 'benchmark.md', 'package.tgz'] as const; +export const PACKAGE_NAME = 'gothic-lock-solver'; +export const REQUIRED_LOCK_COUNT = 45; +export const NPM_REGISTRY = 'https://registry.npmjs.org'; +export const CDN_ORIGIN = 'https://cdn.jsdelivr.net'; + +const COMMIT_SHA = /^[a-f0-9]{40}$/; +const SHA256 = /^[a-f0-9]{64}$/; +const POSITIVE_INTEGER = /^[1-9]\d*$/; +const VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; +const CANARY_SHA_LENGTH = 12; + +export const runtimeFiles = [ + 'gothic-lock-solver.mjs', + 'gothic-lock-solver.min.mjs', + 'gothic-lock-solver.js', + 'gothic-lock-solver.min.js', +] as const; + +export const executableFiles = [...runtimeFiles, 'gothic-lock-solver.cli.mjs'] as const; +export const releaseAssets = [ + ...executableFiles, + 'benchmark-evidence.json', + 'benchmark.json', + 'benchmark.md', + 'package.tgz', +] as const; + export type ReleaseKind = 'stable' | 'canary'; export interface ReleaseManifest { readonly schemaVersion: 1; - readonly name: 'gothic-lock-solver'; + readonly name: typeof PACKAGE_NAME; readonly version: string; readonly sourceSha: string; readonly kind: ReleaseKind; @@ -14,13 +39,21 @@ export interface ReleaseManifest { readonly assets: Readonly>; } +function isRecord(value: unknown): value is Record { + return value !== null && typeof value === 'object' && !Array.isArray(value); +} + export function record(value: unknown, context: string): Record { - if (value === null || typeof value !== 'object' || Array.isArray(value)) throw new Error(`Invalid ${context}`); - return Object.fromEntries(Object.entries(value)); + if (!isRecord(value)) { + throw new Error(`Invalid ${context}`); + } + return value; } export function string(value: unknown, context: string): string { - if (typeof value !== 'string' || value.length === 0) throw new Error(`Invalid ${context}`); + if (typeof value !== 'string' || value.length === 0) { + throw new Error(`Invalid ${context}`); + } return value; } @@ -29,78 +62,167 @@ export function sha256(value: Uint8Array | string): string { } export function assertDigest(bytes: Uint8Array, expected: string, context: string): void { - if (!/^[a-f0-9]{64}$/.test(expected) || sha256(bytes) !== expected) throw new Error(`${context} identity mismatch; existing versions/assets must never be overwritten`); + if (!SHA256.test(expected) || sha256(bytes) !== expected) { + throw new Error(`${context} identity mismatch; existing versions/assets must never be overwritten`); + } } export function parseReleaseVersion(version: string): { version: string; prerelease: boolean } { - const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/.exec(version); - if (!match || match[4]?.split('.').some(part => /^\d+$/.test(part) && part.length > 1 && part.startsWith('0'))) throw new Error(`Invalid release SemVer: ${version}`); - return { version, prerelease: match[4] !== undefined }; + const match = VERSION.exec(version); + const prerelease = match?.[4]; + const hasLeadingZero = prerelease?.split('.').some(part => /^0\d+$/.test(part)); + if (match === null || hasLeadingZero) { + throw new Error(`Invalid release SemVer: ${version}`); + } + return { version, prerelease: prerelease !== undefined }; } export function canaryVersion(base: string, sourceSha: string, runId: string, attempt: string): string { - if (parseReleaseVersion(base).prerelease || !/^[a-f0-9]{40}$/.test(sourceSha) || !/^[1-9]\d*$/.test(runId) || !/^[1-9]\d*$/.test(attempt)) throw new Error('Canary requires stable base version, source SHA and positive run identifiers'); - return `${base}-canary.${runId}.${attempt}.${sourceSha.slice(0, 12)}`; + const validRun = POSITIVE_INTEGER.test(runId) && POSITIVE_INTEGER.test(attempt); + if (parseReleaseVersion(base).prerelease || !COMMIT_SHA.test(sourceSha) || !validRun) { + throw new Error('Canary requires stable base version, source SHA and positive run identifiers'); + } + return `${base}-canary.${runId}.${attempt}.${sourceSha.slice(0, CANARY_SHA_LENGTH)}`; +} + +function compareStableVersions(left: string, right: string): number { + const [leftMajor, leftMinor, leftPatch] = left.split('.').map(BigInt); + const [rightMajor, rightMinor, rightPatch] = right.split('.').map(BigInt); + const components = [ + [leftMajor, rightMajor], + [leftMinor, rightMinor], + [leftPatch, rightPatch], + ] as const; + + for (const [first, second] of components) { + if (first === undefined || second === undefined) { + throw new Error('Missing version component'); + } + if (first !== second) { + return first < second ? -1 : 1; + } + } + return 0; } export function previousStableVersion(versions: readonly string[], current: string): string | null { const currentIsCanary = parseReleaseVersion(current).prerelease; - const compare = (left: string, right: string): number => { - const a = left.split('.')[Symbol.iterator](); const b = right.split('.')[Symbol.iterator](); - for (let index = 0; index < 3; index += 1) { - const first = BigInt(string(a.next().value, 'version component')); - const second = BigInt(string(b.next().value, 'version component')); - if (first !== second) return first < second ? -1 : 1; + const currentBase = string(current.split('-')[0], 'current version base'); + let previous: string | null = null; + + for (const version of versions) { + if (parseReleaseVersion(version).prerelease) { + continue; } - return 0; - }; - const base = current.split('-')[0]; - if (base === undefined) throw new Error('Missing version base'); - return versions.filter(version => !parseReleaseVersion(version).prerelease && compare(version, base) < (currentIsCanary ? 1 : 0)).sort(compare).at(-1) ?? null; + const comparedWithCurrent = compareStableVersions(version, currentBase); + const isEligible = comparedWithCurrent < 0 || (currentIsCanary && comparedWithCurrent === 0); + if (isEligible && (previous === null || compareStableVersions(version, previous) > 0)) { + previous = version; + } + } + return previous; } -export function shouldPromoteGithubLatest(kind: ReleaseKind, version: string, npmLatest: string | null, publishedStableVersions: readonly string[]): boolean { - if (kind !== 'stable' || version !== npmLatest) return false; - if (parseReleaseVersion(version).prerelease) throw new Error('Stable latest must be a stable version'); - return !publishedStableVersions.some(published => previousStableVersion([version], published) === version); +export function shouldPromoteGithubLatest( + kind: ReleaseKind, + version: string, + npmLatest: string | null, + publishedStableVersions: readonly string[], +): boolean { + if (kind !== 'stable' || version !== npmLatest) { + return false; + } + if (parseReleaseVersion(version).prerelease) { + throw new Error('Stable latest must be a stable version'); + } + return !publishedStableVersions.some(published => compareStableVersions(published, version) > 0); } -export function assertReleaseSource(kind: ReleaseKind, ref: string, event: string, repository: string, sourceRepository: string): void { - if (!repository || repository !== sourceRepository || !ref.startsWith('refs/heads/')) throw new Error('Release must use a branch in the owner repository'); - if (kind === 'stable' && (ref !== 'refs/heads/main' || !['push', 'workflow_dispatch'].includes(event))) throw new Error('Stable releases require main push or explicit main resume'); - if (kind === 'canary' && event !== 'workflow_dispatch') throw new Error('Canaries require workflow_dispatch'); +export function assertReleaseSource( + kind: ReleaseKind, + ref: string, + event: string, + repository: string, + sourceRepository: string, +): void { + if (!repository || repository !== sourceRepository || !ref.startsWith('refs/heads/')) { + throw new Error('Release must use a branch in the owner repository'); + } + const stableEvent = event === 'push' || event === 'workflow_dispatch'; + if (kind === 'stable' && (ref !== 'refs/heads/main' || !stableEvent)) { + throw new Error('Stable releases require main push or explicit main resume'); + } + if (kind === 'canary' && event !== 'workflow_dispatch') { + throw new Error('Canaries require workflow_dispatch'); + } } export function validateManifest(value: unknown): ReleaseManifest { const input = record(value, 'release manifest'); - if (input['schemaVersion'] !== 1 || input['name'] !== 'gothic-lock-solver') throw new Error('Unsupported release manifest'); + if (input['schemaVersion'] !== 1 || input['name'] !== PACKAGE_NAME) { + throw new Error('Unsupported release manifest'); + } + const version = string(input['version'], 'version'); const sourceSha = string(input['sourceSha'], 'source SHA'); const kind = input['kind']; const tarballSha256 = string(input['tarballSha256'], 'tarball hash'); - if (!/^[a-f0-9]{40}$/.test(sourceSha) || !/^[a-f0-9]{64}$/.test(tarballSha256) || (kind !== 'stable' && kind !== 'canary')) throw new Error('Invalid release identity'); - if (parseReleaseVersion(version).prerelease !== (kind === 'canary')) throw new Error('Release kind and version disagree'); + if (!COMMIT_SHA.test(sourceSha) || !SHA256.test(tarballSha256) || (kind !== 'stable' && kind !== 'canary')) { + throw new Error('Invalid release identity'); + } + if (parseReleaseVersion(version).prerelease !== (kind === 'canary')) { + throw new Error('Release kind and version disagree'); + } + const rawAssets = record(input['assets'], 'asset hashes'); - if (Object.keys(rawAssets).length !== releaseAssets.length) throw new Error('Incomplete or unexpected release assets'); + if (Object.keys(rawAssets).length !== releaseAssets.length) { + throw new Error('Incomplete or unexpected release assets'); + } const assets: Record = {}; for (const filename of releaseAssets) { const hash = string(rawAssets[filename], `hash for ${filename}`); - if (!/^[a-f0-9]{64}$/.test(hash)) throw new Error(`Invalid asset hash: ${filename}`); + if (!SHA256.test(hash)) { + throw new Error(`Invalid asset hash: ${filename}`); + } assets[filename] = hash; } - if (assets['package.tgz'] !== tarballSha256) throw new Error('Tarball hash disagrees with asset manifest'); - return { schemaVersion: 1, name: 'gothic-lock-solver', version, sourceSha, kind, tarballSha256, assets }; + if (assets['package.tgz'] !== tarballSha256) { + throw new Error('Tarball hash disagrees with asset manifest'); + } + + return { schemaVersion: 1, name: PACKAGE_NAME, version, sourceSha, kind, tarballSha256, assets }; } -export function validateEvidenceGate(value: unknown, evidenceSha256: string, acceptedSha256 = '', acceptanceReason = ''): string | null { +export function validateEvidenceGate( + value: unknown, + evidenceSha256: string, + acceptedSha256 = '', + acceptanceReason = '', +): string | null { const evidence = record(value, 'benchmark evidence'); - if (evidence['schemaVersion'] !== 1 || evidence['quality'] !== 'passed') throw new Error('Release requires passed benchmark quality'); - if (record(evidence['config'], 'benchmark config')['lockCount'] !== 45) throw new Error('Release requires all 45 fixtures'); + if (evidence['schemaVersion'] !== 1 || evidence['quality'] !== 'passed') { + throw new Error('Release requires passed benchmark quality'); + } + const config = record(evidence['config'], 'benchmark config'); + if (config['lockCount'] !== REQUIRED_LOCK_COUNT) { + throw new Error(`Release requires all ${REQUIRED_LOCK_COUNT} fixtures`); + } + const memory = record(evidence['memory'], 'memory evidence'); - const memoryVerdict = record(memory['performance'], 'memory performance')['verdict']; - const timing = evidence['performance']; - if (![timing, memoryVerdict].every(verdict => ['passed', 'inconclusive', 'regression'].includes(string(verdict, 'performance verdict')))) throw new Error('Unknown performance verdict'); - if (timing === 'passed' && memoryVerdict === 'passed') return null; - if (!/^[a-f0-9]{64}$/.test(evidenceSha256) || acceptedSha256 !== evidenceSha256 || acceptanceReason.trim().length === 0) throw new Error('Release performance needs a passed result or explicit acceptance of this exact evidence SHA with a reason'); + const memoryPerformance = record(memory['performance'], 'memory performance'); + const memoryVerdict = string(memoryPerformance['verdict'], 'memory performance verdict'); + const timingVerdict = string(evidence['performance'], 'timing performance verdict'); + const allowedVerdicts = ['passed', 'inconclusive', 'regression']; + if (!allowedVerdicts.includes(timingVerdict) || !allowedVerdicts.includes(memoryVerdict)) { + throw new Error('Unknown performance verdict'); + } + if (timingVerdict === 'passed' && memoryVerdict === 'passed') { + return null; + } + + const exactEvidenceAccepted = SHA256.test(evidenceSha256) && acceptedSha256 === evidenceSha256; + if (!exactEvidenceAccepted || acceptanceReason.trim().length === 0) { + throw new Error('Release performance needs a passed result or explicit acceptance of this exact evidence SHA with a reason'); + } return acceptanceReason; } diff --git a/.github/scripts/release-network.ts b/.github/scripts/release-network.ts index a3244d7..b64ded0 100644 --- a/.github/scripts/release-network.ts +++ b/.github/scripts/release-network.ts @@ -3,27 +3,58 @@ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; import { setTimeout } from 'node:timers/promises'; -import { assertDigest, record, releaseAssets, runtimeFiles, sha256, shouldPromoteGithubLatest, string, type ReleaseManifest } from './release-lib.ts'; +import { + assertDigest, + CDN_ORIGIN, + NPM_REGISTRY, + PACKAGE_NAME, + record, + releaseAssets, + runtimeFiles, + sha256, + shouldPromoteGithubLatest, + string, + type ReleaseManifest, +} from './release-lib.ts'; + +const REQUEST_TIMEOUT_MS = 30_000; +const DELIVERY_ATTEMPTS = 4; +const RETRY_DELAY_MS = 5_000; +const GITHUB_RESPONSE_LIMIT_BYTES = 16 * 1024 * 1024; +const RELEASES_PER_PAGE = 100; +const STABLE_RELEASE_TAG = /^v(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/; export async function fetchBytes(url: string): Promise { - const response = await fetch(url, { signal: AbortSignal.timeout(30_000) }); - if (!response.ok) throw new Error(`HTTP ${response.status}: ${url}`); + const response = await fetch(url, { signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) }); + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${url}`); + } return Buffer.from(await response.arrayBuffer()); } export async function registryMetadata(version: string): Promise | null> { - const response = await fetch(`https://registry.npmjs.org/gothic-lock-solver/${encodeURIComponent(version)}`, { signal: AbortSignal.timeout(30_000) }); - if (response.status === 404) return null; - if (!response.ok) throw new Error(`npm registry HTTP ${response.status}`); - const value: unknown = await response.json(); - return record(value, 'npm metadata'); + const url = `${NPM_REGISTRY}/${PACKAGE_NAME}/${encodeURIComponent(version)}`; + const response = await fetch(url, { signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) }); + if (response.status === 404) { + return null; + } + if (!response.ok) { + throw new Error(`npm registry HTTP ${response.status}`); + } + const metadata: unknown = await response.json(); + return record(metadata, 'npm metadata'); } export async function verifyRegistry(manifest: ReleaseManifest): Promise { const metadata = await registryMetadata(manifest.version); - if (!metadata || metadata['name'] !== manifest.name || metadata['version'] !== manifest.version) throw new Error('Exact npm version is not available'); - const tarballUrl = new URL(string(record(metadata['dist'], 'npm dist')['tarball'], 'npm tarball URL')); - if (tarballUrl.origin !== 'https://registry.npmjs.org' || !tarballUrl.pathname.startsWith('/gothic-lock-solver/-/')) throw new Error('Unexpected npm tarball origin/path'); + if (!metadata || metadata['name'] !== manifest.name || metadata['version'] !== manifest.version) { + throw new Error('Exact npm version is not available'); + } + const distribution = record(metadata['dist'], 'npm distribution'); + const tarballUrl = new URL(string(distribution['tarball'], 'npm tarball URL')); + if (tarballUrl.origin !== NPM_REGISTRY || !tarballUrl.pathname.startsWith(`/${PACKAGE_NAME}/-/`)) { + throw new Error('Unexpected npm tarball origin/path'); + } assertDigest(await fetchBytes(tarballUrl.href), manifest.tarballSha256, 'published npm tarball'); } @@ -31,93 +62,207 @@ export async function publishOrResumeNpm(directory: string, manifest: ReleaseMan const existing = await registryMetadata(manifest.version); if (existing) { await verifyRegistry(manifest); - await recordDeliveryStatus(directory, 'npm-existing-verified', 'Resuming exact existing npm bytes; no npm publish was invoked.'); - } else { - await recordDeliveryStatus(directory, 'npm-publish-started', 'If publication succeeds and later delivery fails, rerun the failed jobs using this same prepared artifact.'); - execFileSync('npm', ['publish', resolve(directory, 'package.tgz'), '--access', 'public', '--tag', manifest.kind === 'stable' ? 'latest' : 'canary', '--provenance', '--ignore-scripts', '--registry', 'https://registry.npmjs.org/'], { stdio: 'inherit' }); - await recordDeliveryStatus(directory, 'npm-published-awaiting-verification', 'npm publication completed; registry/CDN and GitHub release remain pending.'); + console.log(`Resuming identical npm ${manifest.version}; no publication was needed.`); + return; } + + execFileSync('npm', [ + 'publish', resolve(directory, 'package.tgz'), + '--access', 'public', + '--tag', manifest.kind === 'stable' ? 'latest' : 'canary', + '--provenance', + '--ignore-scripts', + '--registry', `${NPM_REGISTRY}/`, + ], { stdio: 'inherit' }); } -/** Retries only delivery verification; publication is never retried here. */ +/** Registry/CDN propagation may lag publication. Never retry publication here. */ export async function retryDelivery(action: () => Promise): Promise { let lastError: unknown; - for (let attempt = 0; attempt < 4; attempt += 1) { - try { await action(); return; } catch (error) { lastError = error; } - if (attempt < 3) await setTimeout(5_000 * (attempt + 1)); + for (let attempt = 1; attempt <= DELIVERY_ATTEMPTS; attempt += 1) { + try { + await action(); + return; + } catch (error) { + lastError = error; + } + if (attempt < DELIVERY_ATTEMPTS) { + await setTimeout(RETRY_DELAY_MS * attempt); + } } throw lastError; } export async function verifyCdn(manifest: ReleaseManifest): Promise { for (const filename of runtimeFiles) { - const url = `https://cdn.jsdelivr.net/npm/${manifest.name}@${manifest.version}/dist/${filename}`; - const response = await fetch(url, { headers: { Origin: 'https://example.com' }, signal: AbortSignal.timeout(30_000) }); - if (!response.ok || !/(?:java|ecma)script/i.test(response.headers.get('content-type') ?? '') || response.headers.get('access-control-allow-origin') !== '*') throw new Error(`CDN delivery headers/status rejected: ${filename}`); - assertDigest(Buffer.from(await response.arrayBuffer()), string(manifest.assets[filename], filename), `CDN ${filename}`); + const url = `${CDN_ORIGIN}/npm/${manifest.name}@${manifest.version}/dist/${filename}`; + const response = await fetch(url, { + headers: { Origin: 'https://example.com' }, + signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), + }); + const contentType = response.headers.get('content-type') ?? ''; + const allowsCrossOrigin = response.headers.get('access-control-allow-origin') === '*'; + if (!response.ok || !/(?:java|ecma)script/i.test(contentType) || !allowsCrossOrigin) { + throw new Error(`CDN delivery headers/status rejected: ${filename}`); + } + const expectedHash = string(manifest.assets[filename], filename); + assertDigest(Buffer.from(await response.arrayBuffer()), expectedHash, `CDN ${filename}`); } } -function gh(args: readonly string[]): string { - return execFileSync('gh', [...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 16 * 1024 * 1024 }); +function github(args: readonly string[]): string { + return execFileSync('gh', [...args], { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + maxBuffer: GITHUB_RESPONSE_LIMIT_BYTES, + }); } -/** Uploads missing assets only. Existing npm versions and GitHub assets are immutable inputs. */ -export async function completeGithubRelease(directory: string, manifest: ReleaseManifest, repository: string): Promise { - if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository)) throw new Error('Invalid repository'); - const tag = `v${manifest.version}`; - // Create the exact tag explicitly: a draft release need not have materialized its tag yet. - const matchingRefs: unknown = JSON.parse(gh(['api', `repos/${repository}/git/matching-refs/tags/${tag}`])); - if (!Array.isArray(matchingRefs)) throw new Error('Invalid tag refs response'); - if (!matchingRefs.map(value => record(value, 'tag ref')).some(value => value['ref'] === `refs/tags/${tag}`)) { - gh(['api', '--method', 'POST', `repos/${repository}/git/refs`, '-f', `ref=refs/tags/${tag}`, '-f', `sha=${manifest.sourceSha}`]); - } - const ref: unknown = JSON.parse(gh(['api', `repos/${repository}/git/ref/tags/${tag}`])); - let object = record(record(ref, 'tag ref')['object'], 'tag object'); - for (let depth = 0; object['type'] === 'tag' && depth < 5; depth += 1) { - const annotated: unknown = JSON.parse(gh(['api', `repos/${repository}/git/tags/${string(object['sha'], 'tag SHA')}`])); - object = record(record(annotated, 'annotated tag')['object'], 'tag target'); - } - if (object['type'] !== 'commit' || object['sha'] !== manifest.sourceSha) throw new Error('Existing release tag does not identify the prepared source'); - const pages: unknown = JSON.parse(gh(['api', `repos/${repository}/releases?per_page=100`, '--paginate', '--slurp'])); - if (!Array.isArray(pages)) throw new Error('Invalid release pages response'); - const releases: unknown[] = pages.flatMap((page: unknown): unknown[] => { - if (!Array.isArray(page)) throw new Error('Invalid releases page'); - return page; +function githubJson(args: readonly string[]): unknown { + const value: unknown = JSON.parse(github(args)); + return value; +} + +function ensureSourceTag(repository: string, tag: string, sourceSha: string): void { + const matchingRefs = githubJson(['api', `repos/${repository}/git/matching-refs/tags/${tag}`]); + if (!Array.isArray(matchingRefs)) { + throw new Error('Invalid tag refs response'); + } + const tagExists = matchingRefs.some(value => record(value, 'tag ref')['ref'] === `refs/tags/${tag}`); + if (!tagExists) { + github([ + 'api', '--method', 'POST', `repos/${repository}/git/refs`, + '-f', `ref=refs/tags/${tag}`, + '-f', `sha=${sourceSha}`, + ]); + } + + // The commit endpoint resolves lightweight and annotated tags for us. + const commit = record(githubJson(['api', `repos/${repository}/commits/refs/tags/${tag}`]), 'tag commit'); + if (commit['sha'] !== sourceSha) { + throw new Error('Existing release tag does not identify the prepared source'); + } +} + +function publishedReleases(repository: string): readonly Record[] { + const pages = githubJson([ + 'api', `repos/${repository}/releases?per_page=${RELEASES_PER_PAGE}`, + '--paginate', '--slurp', + ]); + if (!Array.isArray(pages)) { + throw new Error('Invalid release pages response'); + } + return pages.flatMap((page: unknown) => { + if (!Array.isArray(page)) { + throw new Error('Invalid releases page'); + } + return page.map(value => record(value, 'GitHub release')); }); - const existing = releases.map(value => record(value, 'release')).find(value => value['tag_name'] === tag); - if (!existing) { - gh(['release', 'create', tag, '--repo', repository, '--verify-tag', '--target', manifest.sourceSha, '--draft', '--title', tag, - '--notes-file', join(directory, 'release-notes.md'), ...(manifest.kind === 'canary' ? ['--prerelease', '--latest=false'] : [])]); - } - const release: unknown = JSON.parse(gh(['api', `repos/${repository}/releases/tags/${tag}`])); - const releaseRecord = record(release, 'GitHub release'); - if (releaseRecord['prerelease'] !== (manifest.kind === 'canary')) throw new Error('Existing release kind differs'); - const assets = releaseRecord['assets']; - if (!Array.isArray(assets)) throw new Error('Invalid GitHub assets'); +} + +function uploadMissingAssets( + directory: string, + repository: string, + tag: string, + release: Record, +): void { + const assets = release['assets']; + if (!Array.isArray(assets)) { + throw new Error('Invalid GitHub assets'); + } + const existingNames = new Set(assets.map(value => record(value, 'release asset')['name'])); const expectedFiles = [...releaseAssets, 'release-manifest.json']; - for (const filename of expectedFiles) { - const expectedBytes = await readFile(join(directory, filename)); - const asset = assets.map(value => record(value, 'asset')).find(value => value['name'] === filename); - if (!asset) { - if (releaseRecord['draft'] !== true) throw new Error('Published release has a missing asset; refusing to mutate it'); - gh(['release', 'upload', tag, join(directory, filename), '--repo', repository]); + const missingFiles = expectedFiles.filter(filename => !existingNames.has(filename)); + if (missingFiles.length === 0) { + return; + } + if (release['draft'] !== true) { + throw new Error('Published release has a missing asset; refusing to mutate it'); + } + github([ + 'release', 'upload', tag, + ...missingFiles.map(filename => join(directory, filename)), + '--repo', repository, + ]); +} + +async function verifyGithubAssets(directory: string, repository: string, tag: string): Promise { + const temporary = await mkdtemp(join(tmpdir(), 'gothic-release-assets-')); + const expectedFiles = [...releaseAssets, 'release-manifest.json']; + try { + github([ + 'release', 'download', tag, + '--repo', repository, + ...expectedFiles.flatMap(filename => ['--pattern', filename]), + '--dir', temporary, + ]); + for (const filename of expectedFiles) { + const expectedBytes = await readFile(join(directory, filename)); + const downloadedBytes = await readFile(join(temporary, filename)); + assertDigest(downloadedBytes, sha256(expectedBytes), `GitHub ${filename}`); } - const temp = await mkdtemp(join(tmpdir(), 'gothic-release-asset-')); - try { - gh(['release', 'download', tag, '--repo', repository, '--pattern', filename, '--dir', temp]); - assertDigest(await readFile(join(temp, filename)), sha256(expectedBytes), `GitHub ${filename}`); - } finally { await rm(temp, { recursive: true, force: true }); } + } finally { + await rm(temporary, { recursive: true, force: true }); + } +} + +function stableReleaseVersions(releases: readonly Record[]): readonly string[] { + return releases + .filter(release => release['draft'] === false && release['prerelease'] === false) + .map(release => string(release['tag_name'], 'release tag')) + .filter(tag => STABLE_RELEASE_TAG.test(tag)) + .map(tag => tag.slice(1)); +} + +/** Resume partial drafts, but never overwrite existing npm versions or assets. */ +export async function completeGithubRelease( + directory: string, + manifest: ReleaseManifest, + repository: string, +): Promise { + if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository)) { + throw new Error('Invalid repository'); } - if (releaseRecord['draft'] === true) { - const npmLatest = await registryMetadata('latest'); - const publishedStableVersions = releases.map(value => record(value, 'published release')).filter(value => value['draft'] === false && value['prerelease'] === false) - .map(value => string(value['tag_name'], 'release tag')).filter(value => /^v(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/.test(value)).map(value => value.slice(1)); - const latest = shouldPromoteGithubLatest(manifest.kind, manifest.version, npmLatest ? string(npmLatest['version'], 'npm latest version') : null, publishedStableVersions); - gh(['release', 'edit', tag, '--repo', repository, '--draft=false', `--latest=${latest}`]); + const tag = `v${manifest.version}`; + ensureSourceTag(repository, tag, manifest.sourceSha); + + const releases = publishedReleases(repository); + const existing = releases.find(release => release['tag_name'] === tag); + if (!existing) { + github([ + 'release', 'create', tag, + '--repo', repository, + '--verify-tag', + '--target', manifest.sourceSha, + '--draft', + '--title', tag, + '--notes-file', join(directory, 'release-notes.md'), + ...(manifest.kind === 'canary' ? ['--prerelease', '--latest=false'] : []), + ]); + } + + const release = record(githubJson(['api', `repos/${repository}/releases/tags/${tag}`]), 'GitHub release'); + if (release['prerelease'] !== (manifest.kind === 'canary')) { + throw new Error('Existing release kind differs'); } + uploadMissingAssets(directory, repository, tag, release); + await verifyGithubAssets(directory, repository, tag); + if (release['draft'] !== true) { + return; + } + + const npmMetadata = await registryMetadata('latest'); + const npmLatest = npmMetadata ? string(npmMetadata['version'], 'npm latest version') : null; + const promoteLatest = shouldPromoteGithubLatest(manifest.kind, manifest.version, npmLatest, stableReleaseVersions(releases)); + github([ + 'release', 'edit', tag, + '--repo', repository, + '--draft=false', + `--latest=${promoteLatest}`, + ]); } export async function recordDeliveryStatus(directory: string, stage: string, detail: string): Promise { - await writeFile(join(directory, 'delivery-status.json'), `${JSON.stringify({ stage, detail, recordedAt: new Date().toISOString() }, null, 2)}\n`); + const status = { stage, detail, recordedAt: new Date().toISOString() }; + await writeFile(join(directory, 'delivery-status.json'), `${JSON.stringify(status, null, 2)}\n`); } diff --git a/.github/scripts/release.ts b/.github/scripts/release.ts index 17763c5..24d0838 100644 --- a/.github/scripts/release.ts +++ b/.github/scripts/release.ts @@ -1,96 +1,223 @@ import { execFileSync } from 'node:child_process'; -import { appendFile, readFile, writeFile } from 'node:fs/promises'; +import { appendFile, writeFile } from 'node:fs/promises'; import { resolve } from 'node:path'; -import { assertReleaseSource, canaryVersion, record, string, type ReleaseKind } from './release-lib.ts'; -import { prepareRelease, readJson, verifyPreparedRelease, type EvidenceAcceptance } from './release-files.ts'; -import { completeGithubRelease, publishOrResumeNpm, recordDeliveryStatus, retryDelivery, verifyCdn, verifyRegistry } from './release-network.ts'; +import { parseArgs } from 'node:util'; +import { + assertReleaseSource, + canaryVersion, + record, + string, + type ReleaseKind, + type ReleaseManifest, +} from './release-lib.ts'; +import { + prepareRelease, + readJson, + verifyPreparedRelease, + type EvidenceAcceptance, +} from './release-files.ts'; +import { + completeGithubRelease, + publishOrResumeNpm, + recordDeliveryStatus, + retryDelivery, + verifyCdn, + verifyRegistry, +} from './release-network.ts'; -function env(name: string): string { return string(process.env[name], name); } +const NPM_VERSION = '12.0.2'; +const GITHUB_CLI_VERSION = '2.100.0'; +const MAIN_REF = 'refs/heads/main'; +const USAGE = 'Usage: node .github/scripts/release.ts canary-version|prepare|check|publish|verify|github [--directory PATH] [--benchmark-dir PATH] [--source-sha SHA]'; + +function environmentValue(name: string): string { + return string(process.env[name], name); +} function workflowAcceptance(): EvidenceAcceptance { - return { acceptedEvidenceSha256: process.env['PERFORMANCE_ACCEPTED_EVIDENCE_SHA256'] ?? '', - acceptanceReason: process.env['PERFORMANCE_ACCEPTANCE_REASON'] ?? '' }; + return { + acceptedEvidenceSha256: process.env['PERFORMANCE_ACCEPTED_EVIDENCE_SHA256'] ?? '', + acceptanceReason: process.env['PERFORMANCE_ACCEPTANCE_REASON'] ?? '', + }; } -function kind(): ReleaseKind { +function releaseKind(): ReleaseKind { const value = process.env['RELEASE_KIND'] ?? 'stable'; - if (value !== 'stable' && value !== 'canary') throw new Error('Invalid RELEASE_KIND'); + if (value !== 'stable' && value !== 'canary') { + throw new Error('Invalid RELEASE_KIND'); + } return value; } function requirePublicationContext(): void { - if (process.env['NPM_PUBLICATION_READY'] !== 'true' || process.env['GITHUB_ACTIONS'] !== 'true') throw new Error('Publication readiness is not configured; prepare/verify are available locally'); - const repository = env('GITHUB_REPOSITORY'); - if (process.env['GITHUB_REF'] !== 'refs/heads/main' || process.env['GITHUB_WORKFLOW_REF'] !== `${repository}/.github/workflows/release.yml@refs/heads/main`) throw new Error('Privileged release code must come from the main workflow'); - assertReleaseSource(kind(), env('RELEASE_SOURCE_REF'), env('GITHUB_EVENT_NAME'), repository, env('RELEASE_SOURCE_REPOSITORY')); + if (process.env['NPM_PUBLICATION_READY'] !== 'true' || process.env['GITHUB_ACTIONS'] !== 'true') { + throw new Error('Publication readiness is not configured; prepare/verify are available locally'); + } + + const repository = environmentValue('GITHUB_REPOSITORY'); + const expectedWorkflow = `${repository}/.github/workflows/release.yml@${MAIN_REF}`; + if (process.env['GITHUB_REF'] !== MAIN_REF || process.env['GITHUB_WORKFLOW_REF'] !== expectedWorkflow) { + throw new Error('Privileged release code must come from the main workflow'); + } + assertReleaseSource( + releaseKind(), + environmentValue('RELEASE_SOURCE_REF'), + environmentValue('GITHUB_EVENT_NAME'), + repository, + environmentValue('RELEASE_SOURCE_REPOSITORY'), + ); } -const [command, ...args] = process.argv.slice(2); -const fields = new Map(); -for (let index = 0; index < args.length; index += 2) { - const key = args[index]; const value = args[index + 1]; - if (!key || !value || !['--directory', '--benchmark-dir', '--source-sha'].includes(key) || fields.has(key)) throw new Error('Invalid release arguments'); - fields.set(key, value); +function requirePreparedSource(manifest: ReleaseManifest): void { + if (manifest.kind !== releaseKind() || manifest.sourceSha !== environmentValue('RELEASE_SOURCE_SHA')) { + throw new Error('Prepared release does not match the verified workflow source'); + } } -const directory = resolve(fields.get('--directory') ?? 'artifacts/release'); -try { - switch (command) { - case 'canary-version': { - const path = resolve('package.json'); const metadata = record(await readJson(path), 'package metadata'); - metadata['version'] = canaryVersion(string(metadata['version'], 'package version'), env('RELEASE_SOURCE_SHA'), env('GITHUB_RUN_ID'), env('GITHUB_RUN_ATTEMPT')); - await writeFile(path, `${JSON.stringify(metadata, null, 2)}\n`); - console.log(metadata['version']); - break; +function parseReleaseArguments() { + const parsed = parseArgs({ + allowPositionals: true, + tokens: true, + options: { + directory: { type: 'string' }, + 'benchmark-dir': { type: 'string' }, + 'source-sha': { type: 'string' }, + }, + }); + if (parsed.positionals.length !== 1) { + throw new Error(USAGE); + } + + const suppliedOptions = new Set(); + for (const token of parsed.tokens) { + if (token.kind !== 'option') { + continue; + } + if (!token.value) { + throw new Error(`Empty release option: --${token.name}`); + } + if (suppliedOptions.has(token.name)) { + throw new Error(`Repeated release option: --${token.name}`); } + suppliedOptions.add(token.name); + } + return { + command: parsed.positionals[0], + directory: resolve(parsed.values.directory ?? 'artifacts/release'), + benchmarkDirectory: parsed.values['benchmark-dir'], + sourceSha: parsed.values['source-sha'], + }; +} + +async function assignCanaryVersion(): Promise { + const path = resolve('package.json'); + const metadata = record(await readJson(path), 'package metadata'); + metadata['version'] = canaryVersion( + string(metadata['version'], 'package version'), + environmentValue('RELEASE_SOURCE_SHA'), + environmentValue('GITHUB_RUN_ID'), + environmentValue('GITHUB_RUN_ATTEMPT'), + ); + await writeFile(path, `${JSON.stringify(metadata, null, 2)}\n`); + console.log(metadata['version']); +} + +async function publishPreparedRelease(directory: string): Promise { + requirePublicationContext(); + const manifest = await verifyPreparedRelease(directory, workflowAcceptance()); + requirePreparedSource(manifest); + + const npmVersion = execFileSync('npm', ['--version'], { encoding: 'utf8' }).trim(); + if (npmVersion !== NPM_VERSION) { + throw new Error(`Expected pinned npm CLI ${NPM_VERSION}`); + } + await publishOrResumeNpm(directory, manifest); +} + +async function verifyDelivery(directory: string): Promise { + const manifest = await verifyPreparedRelease(directory, workflowAcceptance()); + await retryDelivery(async () => { + await verifyRegistry(manifest); + await verifyCdn(manifest); + }); + const { verifyCdnBrowsers } = await import('./release-browser.ts'); + await verifyCdnBrowsers(manifest); + await recordDeliveryStatus( + directory, + 'registry-cdn-verified', + 'Exact npm tarball and four CDN files verified; Chromium, Firefox and WebKit passed all 45 fixtures.', + ); +} + +async function finalizeGithubRelease(directory: string): Promise { + requirePublicationContext(); + const manifest = await verifyPreparedRelease(directory, workflowAcceptance()); + requirePreparedSource(manifest); + + const githubVersion = execFileSync('gh', ['--version'], { encoding: 'utf8' }); + if (!githubVersion.startsWith(`gh version ${GITHUB_CLI_VERSION} `)) { + throw new Error(`Expected pinned GitHub CLI ${GITHUB_CLI_VERSION}`); + } + const status = record(await readJson(resolve(directory, 'delivery-status.json')), 'delivery status'); + if (status['stage'] !== 'registry-cdn-verified') { + throw new Error('GitHub publication requires completed registry/CDN/browser verification'); + } + await completeGithubRelease(directory, manifest, environmentValue('GITHUB_REPOSITORY')); + await recordDeliveryStatus(directory, 'complete', 'npm, CDN browser delivery and GitHub Release verified.'); +} + +async function runReleaseCommand(): Promise { + const { command, directory, benchmarkDirectory, sourceSha } = parseReleaseArguments(); + switch (command) { + case 'canary-version': + await assignCanaryVersion(); + return; case 'prepare': { - const sourceSha = fields.get('--source-sha') ?? execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(); - const manifest = await prepareRelease({ root: process.cwd(), outputDir: directory, - benchmarkDir: resolve(string(fields.get('--benchmark-dir'), '--benchmark-dir')), sourceSha, kind: kind(), - ...workflowAcceptance() }); + const revision = sourceSha ?? execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(); + const manifest = await prepareRelease({ + root: process.cwd(), + outputDir: directory, + benchmarkDir: resolve(string(benchmarkDirectory, '--benchmark-dir')), + sourceSha: revision, + kind: releaseKind(), + ...workflowAcceptance(), + }); console.log(JSON.stringify(manifest)); - if (process.env['GITHUB_OUTPUT']) await appendFile(process.env['GITHUB_OUTPUT'], `version=${manifest.version}\nsource-sha=${manifest.sourceSha}\n`); - break; + const outputPath = process.env['GITHUB_OUTPUT']; + if (outputPath) { + await appendFile(outputPath, `version=${manifest.version}\nsource-sha=${manifest.sourceSha}\n`); + } + return; } - case 'check': { + case 'check': console.log(JSON.stringify(await verifyPreparedRelease(directory, workflowAcceptance()))); - break; - } - case 'publish': { - requirePublicationContext(); - const manifest = await verifyPreparedRelease(directory, workflowAcceptance()); - if (manifest.kind !== kind() || manifest.sourceSha !== env('RELEASE_SOURCE_SHA')) throw new Error('Prepared release does not match the verified workflow source'); - if (execFileSync('npm', ['--version'], { encoding: 'utf8' }).trim() !== '12.0.2') throw new Error('Expected pinned npm CLI 12.0.2'); - await publishOrResumeNpm(directory, manifest); - break; - } - case 'verify': { - const manifest = await verifyPreparedRelease(directory, workflowAcceptance()); - await retryDelivery(async () => { await verifyRegistry(manifest); await verifyCdn(manifest); }); - const { verifyCdnBrowsers } = await import('./release-browser.ts'); - await verifyCdnBrowsers(manifest); - await recordDeliveryStatus(directory, 'registry-cdn-verified', 'Exact npm tarball and four CDN files verified; Chromium, Firefox and WebKit passed all 45 fixtures.'); - break; - } - case 'github': { - requirePublicationContext(); - const manifest = await verifyPreparedRelease(directory, workflowAcceptance()); - if (manifest.kind !== kind() || manifest.sourceSha !== env('RELEASE_SOURCE_SHA')) throw new Error('Prepared GitHub release source mismatch'); - if (!execFileSync('gh', ['--version'], { encoding: 'utf8' }).startsWith('gh version 2.100.0 ')) throw new Error('Expected pinned GitHub CLI 2.100.0'); - const status = record(JSON.parse(await readFile(resolve(directory, 'delivery-status.json'), 'utf8')), 'delivery status'); - if (status['stage'] !== 'registry-cdn-verified') throw new Error('GitHub publication requires completed registry/CDN/browser verification'); - await completeGithubRelease(directory, manifest, env('GITHUB_REPOSITORY')); - await recordDeliveryStatus(directory, 'complete', 'npm, CDN browser delivery and GitHub Release verified.'); - break; - } - default: throw new Error('Usage: node .github/scripts/release.ts canary-version|prepare|check|publish|verify|github [--directory PATH] [--benchmark-dir PATH] [--source-sha SHA]'); + return; + case 'publish': + await publishPreparedRelease(directory); + return; + case 'verify': + await verifyDelivery(directory); + return; + case 'github': + await finalizeGithubRelease(directory); + return; + default: + throw new Error(USAGE); } +} + +try { + await runReleaseCommand(); } catch (error) { - const detail = error instanceof Error ? error.message : String(error); - console.error(detail); - if (['publish', 'verify', 'github'].includes(command ?? '')) { - console.error('Release incomplete. npm may already contain this exact version; preserve prepared artifacts and rerun failed jobs. Never overwrite/unpublish to recover.'); - if (process.env['GITHUB_STEP_SUMMARY']) await appendFile(process.env['GITHUB_STEP_SUMMARY'], `\nRelease incomplete at ${command}. Preserve the prepared artifact and rerun failed jobs. Existing versions/assets are verified, never overwritten.\n`); + console.error(error instanceof Error ? error.message : String(error)); + const command = process.argv[2]; + if (command === 'publish' || command === 'verify' || command === 'github') { + const recovery = 'Release incomplete. Preserve the prepared artifacts and rerun failed jobs. Existing npm versions and assets are verified, never overwritten.'; + console.error(recovery); + const summaryPath = process.env['GITHUB_STEP_SUMMARY']; + if (summaryPath) { + await appendFile(summaryPath, `\n${recovery}\n`); + } } process.exitCode = 1; } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 0e32843..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,179 +0,0 @@ -name: CI - -on: - pull_request: - push: - branches: [main] - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: ci-${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - validation: - name: Package, coverage and three browsers - runs-on: ubuntu-24.04 - timeout-minutes: 30 - defaults: - run: - working-directory: candidate - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - path: candidate - persist-credentials: false - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: 22.23.2 - - name: Record Node 22 consumer executable - id: node22 - run: echo "path=$(command -v node)" >> "$GITHUB_OUTPUT" - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: 26.8.1 - - name: Record Node 26 consumer executable - id: node26 - run: echo "path=$(command -v node)" >> "$GITHUB_OUTPUT" - - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - with: - version: 12.3.4 - package_json_file: candidate/package.json - run_install: false - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: 24.19.0 - cache: pnpm - cache-dependency-path: candidate/pnpm-lock.yaml - - name: Install pinned npm and frozen dependencies - run: | - npm install --global --ignore-scripts npm@12.0.2 - pnpm install --frozen-lockfile - node --version - pnpm --version - npm --version - - name: Install actual browser engines - run: pnpm exec playwright install --with-deps chromium firefox webkit - - name: Lint - run: pnpm lint - - name: Strict type check - run: pnpm typecheck - - name: Node tests and enforced coverage - run: pnpm test:node --coverage - - name: Build all five executable files - run: pnpm build - - name: Distribution consumers - run: pnpm test:distribution - - name: Chromium, Firefox and WebKit unit tests - run: pnpm test:browser - - name: HTTP, module, classic, file and Worker consumers - run: pnpm test:e2e - - name: Verify one archive in Node 22, 24 and 26 and all browsers - env: - NODE22: ${{ steps.node22.outputs.path }} - NODE26: ${{ steps.node26.outputs.path }} - run: pnpm verify:package --node "$NODE22" --node "$NODE26" - - name: Preserve test, coverage and distribution evidence - if: always() - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: ci-validation - path: | - candidate/dist/ - candidate/artifacts/package/ - candidate/artifacts/coverage/ - candidate/artifacts/playwright-report/ - candidate/artifacts/test-results/ - if-no-files-found: warn - retention-days: 30 - - node26: - name: Node 26 source tests - runs-on: ubuntu-24.04 - timeout-minutes: 15 - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - with: - version: 12.3.4 - run_install: false - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: 26.8.1 - cache: pnpm - - run: pnpm install --frozen-lockfile - - run: pnpm test:node - - benchmark: - name: Paired 45-lock target comparison - needs: validation - runs-on: ubuntu-24.04 - timeout-minutes: 30 - defaults: - run: - working-directory: candidate - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - path: candidate - persist-credentials: false - - name: Check out the actual PR target commit separately - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.event.pull_request.base.sha || '3cbbeedbb409a2d8ffa5b00c778cfcb0b07c0ec6' }} - path: baseline - persist-credentials: false - - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - with: - version: 12.3.4 - package_json_file: candidate/package.json - run_install: false - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: 24.19.0 - cache: pnpm - cache-dependency-path: candidate/pnpm-lock.yaml - - run: pnpm install --frozen-lockfile - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: ci-validation - path: candidate - - name: Build a TypeScript target in the same runtime when needed - run: | - if [ ! -f ../baseline/src/index.mjs ]; then - pnpm --dir ../baseline install --frozen-lockfile - pnpm --dir ../baseline build - fi - - name: Generate baseline from the separately checked-out source - id: baseline - run: node .github/scripts/ci-baseline.ts checkout ../baseline artifacts/baseline - - name: Compare exact built candidate and target, with isolated RSS samples - env: - BASELINE_MODULE: ${{ steps.baseline.outputs.module }} - BASELINE_SNAPSHOT: ${{ steps.baseline.outputs.snapshot }} - BASELINE_FORMAT: ${{ steps.baseline.outputs.format }} - run: pnpm benchmark:compare --candidate-module dist/gothic-lock-solver.mjs --baseline-module "$BASELINE_MODULE" --baseline-snapshot "$BASELINE_SNAPSHOT" --baseline-format "$BASELINE_FORMAT" --output-dir artifacts/benchmark - - name: Show benchmark verdict and measurements - if: always() - run: | - for report in artifacts/benchmark/run-*/benchmark.md; do - if [ -f "$report" ]; then cat "$report" >> "$GITHUB_STEP_SUMMARY"; fi - done - - name: Preserve raw benchmark and baseline evidence - if: always() - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: ci-benchmark - path: | - candidate/artifacts/benchmark/ - candidate/artifacts/baseline/ - if-no-files-found: warn - retention-days: 30 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c287c9e..c678ca5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,7 @@ -name: Release +name: CI on: + pull_request: push: branches: [main] workflow_dispatch: @@ -12,7 +13,7 @@ on: default: canary required: true source_branch: - description: Owner repository branch for a canary; stable always uses main + description: Owner branch for a canary; stable always uses main type: string default: main required: true @@ -20,10 +21,9 @@ on: permissions: contents: read -# Keep partial releases recoverable; a newer run never cancels publication midway. concurrency: - group: release - cancel-in-progress: false + group: ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: RELEASE_KIND: ${{ github.event_name == 'workflow_dispatch' && inputs.kind || 'stable' }} @@ -31,49 +31,52 @@ env: PERFORMANCE_ACCEPTANCE_REASON: ${{ vars.PERFORMANCE_ACCEPTANCE_REASON }} jobs: - build: - name: Build, verify and measure one archive - if: github.ref == 'refs/heads/main' + verification: + name: Tests, package and 45-lock benchmark + if: github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main' runs-on: ubuntu-24.04 timeout-minutes: 45 + defaults: + run: + working-directory: candidate outputs: source-sha: ${{ steps.source.outputs.sha }} source-ref: ${{ steps.source.outputs.ref }} - input-artifact: release-input-${{ github.run_id }}-${{ github.run_attempt }} + artifact: verification-${{ github.run_id }}-${{ github.run_attempt }} benchmark-directory: ${{ steps.report.outputs.directory }} - defaults: - run: - working-directory: candidate steps: - - name: Check out trusted main delivery helpers + - name: Check out the candidate uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - ref: ${{ github.sha }} - path: workflow + ref: ${{ github.event.pull_request.head.sha || (github.event_name == 'workflow_dispatch' && inputs.kind == 'canary' && format('refs/heads/{0}', inputs.source_branch)) || github.sha }} + path: candidate persist-credentials: false - - name: Check out owner repository source without publication credentials + - name: Check out main helpers for release baselines and canary versioning + if: github.event_name != 'pull_request' uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - ref: ${{ github.event_name == 'workflow_dispatch' && inputs.kind == 'canary' && format('refs/heads/{0}', inputs.source_branch) || github.sha }} - path: candidate + ref: ${{ github.sha }} + path: workflow persist-credentials: false - - name: Capture immutable source before running candidate code + - name: Record the source before running candidate code id: source env: - SOURCE_BRANCH: ${{ github.event_name == 'workflow_dispatch' && inputs.kind == 'canary' && inputs.source_branch || 'main' }} + SOURCE_REF: ${{ github.event_name == 'workflow_dispatch' && inputs.kind == 'canary' && format('refs/heads/{0}', inputs.source_branch) || 'refs/heads/main' }} run: | - git check-ref-format "refs/heads/$SOURCE_BRANCH" + git check-ref-format "$SOURCE_REF" echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" - echo "ref=refs/heads/$SOURCE_BRANCH" >> "$GITHUB_OUTPUT" + echo "ref=$SOURCE_REF" >> "$GITHUB_OUTPUT" - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 22.23.2 - - id: node22 + - name: Record Node 22 + id: node22 run: echo "path=$(command -v node)" >> "$GITHUB_OUTPUT" - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 26.8.1 - - id: node26 + - name: Record Node 26 + id: node26 run: echo "path=$(command -v node)" >> "$GITHUB_OUTPUT" - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 with: @@ -85,18 +88,17 @@ jobs: node-version: 24.19.0 cache: pnpm cache-dependency-path: candidate/pnpm-lock.yaml - - name: Install pinned npm and frozen dependencies + - name: Install tools and browsers run: | npm install --global --ignore-scripts npm@12.0.2 pnpm install --frozen-lockfile - - name: Assign a unique canary version before building + pnpm exec playwright install --with-deps chromium firefox webkit + - name: Assign a canary version before building if: env.RELEASE_KIND == 'canary' env: RELEASE_SOURCE_SHA: ${{ steps.source.outputs.sha }} run: node ../workflow/.github/scripts/release.ts canary-version - - name: Install all actual browsers - run: pnpm exec playwright install --with-deps chromium firefox webkit - - name: Check source, coverage, all formats and the one packed archive + - name: Check source, five bundles and one installed archive env: NODE22: ${{ steps.node22.outputs.path }} NODE26: ${{ steps.node26.outputs.path }} @@ -109,209 +111,157 @@ jobs: pnpm test:browser pnpm test:e2e pnpm verify:package --node "$NODE22" --node "$NODE26" - - name: Resolve the preceding stable release from npm and GitHub + - name: Resolve the preceding stable release + if: github.event_name != 'pull_request' id: previous run: | version=$(node -p "JSON.parse(require('node:fs').readFileSync('package.json','utf8')).version") node ../workflow/.github/scripts/ci-baseline.ts release "$version" artifacts/baseline - - name: Check out the explicit first-release reference when needed - if: steps.previous.outputs.available != 'true' + - name: Check out the PR target or first-release reference + if: github.event_name == 'pull_request' || steps.previous.outputs.available != 'true' uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - ref: 3cbbeedbb409a2d8ffa5b00c778cfcb0b07c0ec6 - path: reference-matrix + ref: ${{ github.event.pull_request.base.sha || '3cbbeedbb409a2d8ffa5b00c778cfcb0b07c0ec6' }} + path: baseline persist-credentials: false - - name: Generate a snapshot from the pinned first-release checkout - id: reference - if: steps.previous.outputs.available != 'true' - run: node ../workflow/.github/scripts/ci-baseline.ts checkout ../reference-matrix artifacts/reference-baseline - - name: Measure 45 fixtures against previous stable or explicit reference + - name: Build and measure the separately checked-out baseline + if: github.event_name == 'pull_request' || steps.previous.outputs.available != 'true' + id: baseline + run: | + if [ ! -f ../baseline/src/index.mjs ]; then + pnpm --dir ../baseline install --frozen-lockfile + pnpm --dir ../baseline build + fi + node .github/scripts/ci-baseline.ts checkout ../baseline artifacts/baseline + - name: Compare all 45 locks after other checks have finished id: comparison - # Keep the measured artifact available. The separate prepare job enforces - # quality/performance semantics and exact-evidence acceptance before publish. - continue-on-error: true + # Releases retain failed measurements for explicit evidence acceptance. + # The trusted publication job always enforces quality and performance gates. + continue-on-error: ${{ github.event_name != 'pull_request' }} env: - BASELINE_MODULE: ${{ steps.previous.outputs.module || steps.reference.outputs.module }} - BASELINE_SNAPSHOT: ${{ steps.previous.outputs.snapshot || steps.reference.outputs.snapshot }} - BASELINE_FORMAT: ${{ steps.previous.outputs.format || steps.reference.outputs.format }} - run: pnpm benchmark:compare --candidate-module dist/gothic-lock-solver.mjs --baseline-module "$BASELINE_MODULE" --baseline-snapshot "$BASELINE_SNAPSHOT" --baseline-format "$BASELINE_FORMAT" --output-dir artifacts/benchmark - - name: Require one complete evidence set + BASELINE_MODULE: ${{ steps.previous.outputs.module || steps.baseline.outputs.module }} + BASELINE_SNAPSHOT: ${{ steps.previous.outputs.snapshot || steps.baseline.outputs.snapshot }} + BASELINE_FORMAT: ${{ steps.previous.outputs.format || steps.baseline.outputs.format }} + run: >- + pnpm benchmark:compare + --candidate-module dist/gothic-lock-solver.mjs + --baseline-module "$BASELINE_MODULE" + --baseline-snapshot "$BASELINE_SNAPSHOT" + --baseline-format "$BASELINE_FORMAT" + --output-dir artifacts/benchmark + - name: Record the complete benchmark and show its verdict + if: always() && steps.comparison.outcome != 'skipped' id: report run: | reports=(artifacts/benchmark/run-*) test "${#reports[@]}" -eq 1 test -f "${reports[0]}/benchmark-evidence.json" echo "directory=${reports[0]}" >> "$GITHUB_OUTPUT" - - name: Preserve immutable raw release inputs before the performance gate + cat "${reports[0]}/benchmark.md" >> "$GITHUB_STEP_SUMMARY" + sha256sum "${reports[0]}/benchmark-evidence.json" >> "$GITHUB_STEP_SUMMARY" + - name: Preserve the tested archive, baseline and reports + if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: release-input-${{ github.run_id }}-${{ github.run_attempt }} + name: verification-${{ github.run_id }}-${{ github.run_attempt }} path: | candidate/package.json candidate/README.md candidate/LICENSE candidate/dist/ - candidate/artifacts/package/ - candidate/artifacts/benchmark/ - candidate/artifacts/baseline/ - candidate/artifacts/reference-baseline/ - if-no-files-found: error - retention-days: 30 - - name: Show benchmark and readiness status - if: always() - env: - READY: ${{ vars.NPM_PUBLICATION_READY }} - run: | - for report in artifacts/benchmark/run-*/benchmark.md; do - if [ -f "$report" ]; then cat "$report" >> "$GITHUB_STEP_SUMMARY"; fi - done - for evidence in artifacts/benchmark/run-*/benchmark-evidence.json; do - if [ -f "$evidence" ]; then sha256sum "$evidence" >> "$GITHUB_STEP_SUMMARY"; fi - done - if [ "$READY" != true ]; then - echo 'Publication is disabled. Complete npm ownership/trusted-publisher bootstrap, then configure NPM_PUBLICATION_READY=true. Prepared evidence remains available.' >> "$GITHUB_STEP_SUMMARY" - fi - - name: Preserve test diagnostics even when a check fails - if: always() - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: release-checks-${{ github.run_id }}-${{ github.run_attempt }} - path: | - candidate/artifacts/coverage/ - candidate/artifacts/playwright-report/ - candidate/artifacts/test-results/ - candidate/artifacts/benchmark/ + candidate/artifacts/ if-no-files-found: warn retention-days: 30 - prepare: - name: Validate evidence and prepare exact release assets - needs: build - runs-on: ubuntu-24.04 - timeout-minutes: 5 - outputs: - artifact: release-prepared-${{ github.run_id }}-${{ github.run_attempt }} - version: ${{ steps.prepare.outputs.version }} - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.sha }} - path: workflow - persist-credentials: false - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: 24.19.0 - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: ${{ needs.build.outputs.input-artifact }} - path: candidate - - name: Validate the saved evidence without rebuilding or remeasuring - id: prepare - working-directory: candidate - env: - SOURCE_SHA: ${{ needs.build.outputs.source-sha }} - BENCHMARK_DIRECTORY: ${{ needs.build.outputs.benchmark-directory }} - run: node ../workflow/.github/scripts/release.ts prepare --benchmark-dir "$BENCHMARK_DIRECTORY" --source-sha "$SOURCE_SHA" - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: release-prepared-${{ github.run_id }}-${{ github.run_attempt }} - path: candidate/artifacts/release/ - if-no-files-found: error - retention-days: 30 - - npm: - name: Publish or resume identical npm version - needs: [build, prepare] - if: vars.NPM_PUBLICATION_READY == 'true' + npm-delivery: + name: Publish npm and verify browser delivery + needs: verification + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && vars.NPM_PUBLICATION_READY == 'true' runs-on: ubuntu-24.04 - timeout-minutes: 10 + timeout-minutes: 30 environment: npm-production permissions: contents: read id-token: write + defaults: + run: + working-directory: candidate + outputs: + artifact: delivery-${{ github.run_id }}-${{ github.run_attempt }} env: NPM_PUBLICATION_READY: ${{ vars.NPM_PUBLICATION_READY }} - RELEASE_SOURCE_SHA: ${{ needs.build.outputs.source-sha }} - RELEASE_SOURCE_REF: ${{ needs.build.outputs.source-ref }} + RELEASE_SOURCE_SHA: ${{ needs.verification.outputs.source-sha }} + RELEASE_SOURCE_REF: ${{ needs.verification.outputs.source-ref }} RELEASE_SOURCE_REPOSITORY: ${{ github.repository }} steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.sha }} - persist-credentials: false - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: 24.19.0 - - run: npm install --global --ignore-scripts npm@12.0.2 - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: ${{ needs.prepare.outputs.artifact }} - path: artifacts/release - - run: node .github/scripts/release.ts publish - - name: Preserve partial-publication status - if: always() - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: npm-status-${{ github.run_id }}-${{ github.run_attempt }} - path: artifacts/release/delivery-status.json - if-no-files-found: warn - retention-days: 30 - - verify-delivery: - name: Verify registry bytes and real CDN browsers - needs: [prepare, npm] - runs-on: ubuntu-24.04 - timeout-minutes: 20 - outputs: - artifact: release-verified-${{ github.run_id }}-${{ github.run_attempt }} - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Check out trusted main publication code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.sha }} + path: workflow persist-credentials: false - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 with: version: 12.3.4 + package_json_file: workflow/package.json run_install: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24.19.0 cache: pnpm - - run: pnpm install --frozen-lockfile - - run: pnpm exec playwright install --with-deps chromium firefox webkit + cache-dependency-path: workflow/pnpm-lock.yaml + - name: Install only trusted tooling + working-directory: workflow + run: | + npm install --global --ignore-scripts npm@12.0.2 + pnpm install --frozen-lockfile + pnpm exec playwright install --with-deps chromium firefox webkit - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: ${{ needs.prepare.outputs.artifact }} - path: artifacts/release - - run: node .github/scripts/release.ts verify - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + name: ${{ needs.verification.outputs.artifact }} + path: candidate + - name: Validate saved inputs using trusted main code + env: + BENCHMARK_DIRECTORY: ${{ needs.verification.outputs.benchmark-directory }} + run: >- + node ../workflow/.github/scripts/release.ts prepare + --benchmark-dir "$BENCHMARK_DIRECTORY" + --source-sha "$RELEASE_SOURCE_SHA" + - name: Publish or resume the exact tested npm archive + run: node ../workflow/.github/scripts/release.ts publish + - name: Verify npm bytes, CDN headers and actual browser imports + run: node ../workflow/.github/scripts/release.ts verify + - name: Preserve prepared assets for completion or recovery + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: release-verified-${{ github.run_id }}-${{ github.run_attempt }} - path: artifacts/release/ - if-no-files-found: error + name: delivery-${{ github.run_id }}-${{ github.run_attempt }} + path: candidate/artifacts/release/ + if-no-files-found: warn retention-days: 30 - github: - name: Finalize GitHub release with exact verified assets - needs: [build, prepare, verify-delivery] + github-release: + name: Complete GitHub release with verified assets + needs: [verification, npm-delivery] runs-on: ubuntu-24.04 timeout-minutes: 10 permissions: contents: write env: NPM_PUBLICATION_READY: ${{ vars.NPM_PUBLICATION_READY }} - RELEASE_SOURCE_SHA: ${{ needs.build.outputs.source-sha }} - RELEASE_SOURCE_REF: ${{ needs.build.outputs.source-ref }} + RELEASE_SOURCE_SHA: ${{ needs.verification.outputs.source-sha }} + RELEASE_SOURCE_REF: ${{ needs.verification.outputs.source-ref }} RELEASE_SOURCE_REPOSITORY: ${{ github.repository }} steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Check out trusted main release code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.sha }} persist-credentials: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24.19.0 - - name: Install exact official GitHub CLI release + - name: Install the exact official GitHub CLI release env: GH_ARCHIVE_SHA256: e4d4bb4498e8d007abe545b6568926793ace1b6447da598294a610018cb164be run: | @@ -321,13 +271,13 @@ jobs: echo "$RUNNER_TEMP/gh_2.100.0_linux_amd64/bin" >> "$GITHUB_PATH" - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: ${{ needs.verify-delivery.outputs.artifact }} + name: ${{ needs.npm-delivery.outputs.artifact }} path: artifacts/release - - name: Verify existing assets or finalize the complete draft + - name: Resume missing draft assets or finalize the verified release env: GH_TOKEN: ${{ github.token }} run: node .github/scripts/release.ts github - - name: Preserve completion or recovery status + - name: Preserve the final delivery status if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: diff --git a/.oxlintrc.json b/.oxlintrc.json index c2164ba..ce2bd85 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -25,5 +25,19 @@ "node_modules/**", "artifacts/**", "tests/e2e/distribution/fixtures/**" + ], + "overrides": [ + { + "files": [ + "src/**/*.ts", + ".github/scripts/**/*.ts" + ], + "rules": { + "max-depth": [ + "error", + 3 + ] + } + } ] } diff --git a/AGENTS.md b/AGENTS.md index 1cacada..8730d6f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,6 +29,14 @@ Use domain terms and clear invariant ownership. Prefer composition, small statef classes and plain functions over registries, extra layers or helper-file scaffolding. Apply SOLID, DRY, KISS and YAGNI proportionately. +Use descriptive domain names and one operation per statement. Give domain bounds, +byte sizes and sentinel values names; ordinary loop indices need no constant. +Keep the top-level search/release flow readable in order. Necessary matrix loops +remain explicit, with short bodies; do not hide them in allocation-heavy pipelines. +Oxlint limits block nesting to three levels in `src/` and `.github/scripts/`. +Review responsibilities and data flow as well as tests; passing gates alone does +not establish maintainability. + Use pnpm and exact current stable dependencies with a frozen lockfile. Preserve strict TypeScript, checked indexed access, exact optional properties and `skipLibCheck: false`. No `any`, non-null assertions, `ts-ignore` or unchecked casts. diff --git a/docs/superpowers/plans/2026-09-07-simplify-library.md b/docs/superpowers/plans/2026-09-07-simplify-library.md index ec2db04..3679c30 100644 --- a/docs/superpowers/plans/2026-09-07-simplify-library.md +++ b/docs/superpowers/plans/2026-09-07-simplify-library.md @@ -1,161 +1,58 @@ -# Simplify the library implementation plan - -> **For agentic workers:** Use `superpowers:subagent-driven-development` or `superpowers:executing-plans` to execute these tasks. Checkboxes record progress. The owner has approved execution. Keep working logs and review outputs in ignored `artifacts/`, never in tracked documentation. - -**Goal:** Simplify draft PR #2 into an autonomous TypeScript library with a documented optional solver configuration and CLI configuration file. - -**Architecture:** Production code lives in a flat `src/`; tests and benchmark code live in `tests/`; maintained documentation lives in `wiki/`. GitHub Actions stores generated evidence and publishes the already verified package. Compare only our candidate and our actual target revision or preceding release. - -**Tech stack:** TypeScript, Vite, Vitest, Playwright, Oxlint and pnpm. Recheck official current stable versions before changing dependencies; preserve strict compiler checks. - -**Spec:** The owner-approved decisions below are the specification for this change. They supersede the earlier two-argument-only contract, preserved JavaScript copies, tracked benchmark snapshots and historical documentation layout. No separate specification or review-report file is needed. - -## Approved boundaries - -- Continue the existing `feat/typescript-core-library` branch and draft PR #2, targeting `feat/matrix-astar-benchmarks`. Preserve the reference branch and Git history. Do not merge, publish npm or create a release in this task. -- No references, adapters, copied descriptions or comparisons to other solver projects in the current tracked tree or PR description. Existing approved build tools, official documentation and own npm/CDN/repository URLs remain valid dependencies. -- No tracked `.js`, `.mjs`, `.mts` or generated distribution files. Executable source and configuration use `.ts`; browser fixture HTML may load generated JavaScript. Verify generated bytes without adding a runtime transpiler to integration examples. -- No tracked benchmark snapshots, timing/RSS samples, raw reports, coverage, traces, reviewer reports or copied old implementations. Fixed inputs and mathematical expected values remain test data. -- Node.js 22/24/26 and real Chromium/Firefox/WebKit remain required consumers. Coverage of shipped source requires at least 80% each of statements, branches, functions and lines. -- Keep strict TypeScript, checked indexed access, exact optional properties and `skipLibCheck: false`. No `any`, suppression-based workaround, hidden dependency patch or silent version downgrade. -- Local tests, build, package verification and benchmarks finish before workflow edits. Independent review follows integration; fix findings before updating the PR. - -## Target files and ownership - -| Location | Responsibility | -| --- | --- | -| `src/index.ts` | Public solver facade, named exports and tuple-aware signature | -| `src/cli.ts` | Import-safe CLI entry, input/config/output IO and Russian messages | -| `src/types.ts` | State, links, positions and commands | -| `src/errors.ts` | Invalid-input and resource-exhaustion errors | -| `src/config.ts` | `SolverConfig`, defaults, shared validation and `createSolverConfig` | -| `src/lock.ts` | Validated model, direct effects and safe state representation | -| `src/matrix.ts` | Exact rational analysis | -| `src/astar.ts` | Certificate, matrix-guided exact search and search budget handling | -| `src/bfs.ts` | Exact singular-case search using the same configuration | -| `src/priority-queue.ts` | Indexed priority queue needed by A* | -| `tests/unit/` | Domain, config, CLI, oracle and automation behavior checks | -| `tests/e2e/` | Real browser, Worker, CLI and installed-package consumers, including type consumers | -| `tests/benchmarks/` | Controlled comparison, independent replay, RSS workers, snapshot generation and `fixtures.json` | -| `wiki/` | `api.md`, `configuration.md`, `algorithm.md`, `development.md`, `benchmarks.md`, `releasing.md` | -| `.github/workflows/` | CI and release workflows | -| `.github/scripts/` | Required build/release coordination commands only | -| Root | README, AGENTS, LICENSE, package/lockfile, Git and tool configuration | -| `docs/superpowers/plans/` | This one current plan; no accumulated historical specifications or reports | - -Ignored `dist/` contains five generated runtime choices: readable/minified ESM, readable/minified classic script and standalone Node CLI, plus declarations/checksums. Ignored `artifacts/` contains local results; CI uploads the corresponding outputs. No source changes are generated back into Git during CI. - -## Task 1 — Prove the unpatched toolchain - -**Files:** `package.json`, `pnpm-lock.yaml`, `pnpm-workspace.yaml`, `vitest.config.ts`, TypeScript configs, `patches/`, `.gitattributes`. - -- [x] Record current official stable versions and relevant supported Node ranges from the providers. Use exact resolved versions and lockfile integrity. -- [x] In a disposable install, reproduce strict checking with the Vitest patch removed. Capture the actual declaration errors before selecting a fix. -- [x] Resolve through a supported published version or documented normal configuration/dependency setup. Remove the patch and patch-only workspace/Git settings once this passes. -- [x] Run frozen install, lint, typecheck and a Node/browser Vitest smoke test. Do not reinterpret a failing type check as success. - -Commands: `pnpm install --frozen-lockfile`, `pnpm check`, `pnpm test:node`, then one real-browser project. If the current published toolchain cannot satisfy the agreed strictness without patching, report that concrete incompatibility before proceeding with a substitute or relaxed requirement. This is the first technical gate, not an item deferred until CI. - -## Task 2 — Flatten the core and expose configuration - -**Files:** `src/`, `tests/unit/core*.test.ts`, new `tests/unit/config.test.ts`. - -**Interface:** `createSolverConfig(overrides?: Partial): SolverConfig`; `solveLock(state, links, config?: SolverConfig): readonly Command[] | null`. Preserve the current generic state/link dimension checks. Export the factory and `SolverConfig` through `index.ts`, including the browser namespace build. - -| Config field | Default | Accepted values | -| --- | ---: | --- | -| `maxVisited` | 2,000,000 | Positive safe integer | -| `maxExpanded` | 1,000,000 | Nonnegative safe integer; zero permits no state expansion | -| `maxFrontier` | 1,000,000 | Positive safe integer | -| `maxDenseBytes` | 67,108,864 | Nonnegative safe integer; zero selects sparse storage | - -- [x] Add failing public tests for defaults, partial overrides, freezing, nonmutation, unknown keys, nonnumeric/fractional/unsafe values and invalid complete configs supplied from JavaScript. -- [x] Add public resource-limit tests covering A*, singular BFS, dense/sparse behavior and the certificate path. For example: - -```ts -const config = createSolverConfig({ maxExpanded: 0 }); -expect(Object.isFrozen(config)).toBe(true); -expect(() => solveLock( - [3, 1, 1], - [[0, -1, -1], [-1, 0, -1], [-1, -1, 0]], - config, -)).toThrow(SearchLimitError); -``` - -- [x] Watch each new behavior fail, then implement defaults and shared validation in `config.ts`. The factory accepts partial overrides; the solver accepts the complete readonly config and revalidates ordinary JS input. Missing config uses defaults. Invalid config uses `LockInputError`; exhaustion uses `SearchLimitError`. -- [x] Move/merge the current lock model and prepared state into `lock.ts`; rational analysis into `matrix.ts`; matrix search into `astar.ts`; BFS into `bfs.ts`; heap into `priority-queue.ts`. Fold tiny checked-access helpers and shared budget operations into the modules that own them; do not duplicate budget rules. -- [x] Preserve positions 1–7, target 4, direct noncascading links, zero-based signed commands, exact arithmetic and minimum grouped actions. Configurable budgets do not remove safe-number/representation limits or turn exhaustion into `null`. -- [x] Run unit/oracle tests and strict checking. Compare default-config results with the prior default behavior on every fixed input. - -## Task 3 — Unify CLI and built consumers - -**Files:** old `cli/`, root `solve-lock.mjs`, `src/cli.ts`, `tests/unit/cli.test.ts`, `tests/e2e/`, build/consumer scripts and root Vite/Vitest/Playwright/TypeScript/package configs. - -- [x] First add failing CLI tests for `--config`, omitted/default settings, partial JSON, invalid JSON, unknown fields, missing files and duplicate/unknown flags. -- [x] Consolidate CLI into `src/cli.ts`; use the shared factory and solver. Importing the file in tests must not start the CLI. Preserve standalone execution, npm bin, Russian output, optional `--output`, and exits 0/2/1 for solved/unreachable/error. - -```sh -node dist/gothic-lock-solver.cli.mjs lock.json --config solver.config.json -node dist/gothic-lock-solver.cli.mjs lock.json --config solver.config.json --output solution.json -node dist/gothic-lock-solver.cli.mjs --help -``` - -- [x] Delete the root JavaScript adapter and old `cli/`. Move package/distribution checks into `tests/e2e/`; convert `.mts` type fixtures to `.ts`. -- [x] Replace tracked worker/module `.mjs` fixtures with TS fixture sources compiled to ignored test output through Vite. Keep assertions against the exact generated library bytes in HTTP, classic, module, Worker and file scenarios. -- [x] Move necessary build coordination into `.github/scripts/`; move package verification into `tests/e2e/`. Update scripts, relative URLs, static server paths, test includes and declaration exports together. -- [x] Verify the factory in both ESM files and both classic globals. Add installed positive/negative type-consumer checks for optional config without weakening existing tuple checks. -- [x] Build all five runtime files; run distribution, browser and package/CLI consumers in Node 22/24/26. Measure coverage over all shipped `src/**/*.ts`, including unimported entries. - -## Task 4 — Make benchmarks autonomous and artifact-only - -**Files:** old `benchmarks/`, old `scripts/benchmark*.ts`, `tests/unit/benchmark.test.ts`, `tests/unit/core-oracle.test.ts`, `tests/benchmarks/`, `.gitignore`. - -- [x] Review the existing uncommitted cleanup; verify all 45 numerical state/link configurations and mathematical minima match the prior corpus. Preserve neutral IDs. Do not retain copied descriptions, links, encoded sharing formats or external solution paths. -- [x] Consolidate fixed inputs and expected minimum-action assertions into `tests/benchmarks/fixtures.json`. These are test expectations, not a timing snapshot. Remove redundant manifests, historical commands and committed result snapshots. -- [x] Move measurement/replay/RSS/snapshot code into `tests/benchmarks/`. Remove both vendored implementation trees and the optional original-BFS comparison slot. Retain the independent small-state oracle in unit tests. -- [x] Require an explicit own baseline module for matched comparisons. Generate its snapshot into ignored output by executing that module on the current fixtures. CI supplies the actual PR target checkout; releases supply verified preceding-release bytes. The first release uses the preserved own reference revision. -- [x] Cover missing/mismatched baseline identity, bad paths, worse action/plate/shift metrics, unknown options, source tampering and evidence overwrite in focused tests. Never generate expected baseline quality from the candidate. -- [x] Run candidate and baseline in the same Node environment, alternating order with unchanged-control calibration. Keep timing and memory uncertainty explicit and preserve bounded-repeat observations. Default-config results must retain all 45 known minima. - -Planned local interface (after a separate own baseline checkout is prepared): - -```sh -pnpm benchmark --baseline-module ../gothic-lock-solver-base/src/index.mjs --baseline-format legacy --output-dir artifacts/benchmark -``` - -The legacy path belongs to the preserved separate checkout, not this tracked source tree. For a typed target, build that checkout and pass its `dist/gothic-lock-solver.mjs` with format `tuple`. A single command generates baseline snapshot plus comparison outputs under the selected ignored output directory. Full JSON, Markdown, snapshots and memory samples become CI artifacts and release attachments; no report is copied into Git. - -## Task 5 — Replace documentation with maintained Wiki pages - -**Files:** `README.md`, `AGENTS.md`, `wiki/`, old `docs/`, root configuration references. - -- [x] Move the useful API, mathematical proof/complexity, development, benchmark and release documentation into the six focused Wiki pages listed above. Add the config defaults/ranges/units, factory examples, JS validation, resource behavior and CLI JSON example to `wiki/configuration.md`. -- [x] Keep README to purpose, minimal API/CLI/CDN examples, verification commands and Wiki links. Keep AGENTS to current architecture, invariants and commands; it links the same maintained pages. -- [x] Delete historical specifications, duplicated plans, reviewer reports, benchmark directories and integration proposals. Retain only this active Superpowers plan under `docs/`. -- [x] Check local Markdown links and scan current tracked content for other solver names/URLs/adapters. References to standard tool documentation are permitted; project marketing and examples are self-contained. -- [x] Confirm tracked root directories are only `src`, `tests`, `wiki`, `.github` and the optional active `docs` plan. Remove patch-only and obsolete root helpers/configuration. - -## Task 6 — Run the complete local gate, then adapt workflows - -**Files:** `.github/scripts/`, `.github/workflows/`, automation tests under `tests/unit/`. - -- [x] Before any workflow edits, execute frozen install, `pnpm test` and the full autonomous 45-input benchmark. Verify the same archive in Node 22/24/26 and real Chromium/Firefox/WebKit. Store logs in ignored output. -- [x] Update workflow paths and benchmark inputs to the verified commands. Remove all other-solver checkouts. Keep actual PR-base isolation, official SHA-pinned Actions, read-only candidate execution and separate privileged publication stages. -- [x] Upload test/coverage/browser/benchmark evidence from ignored outputs. Store snapshots only as CI artifacts or GitHub Release assets. Release comparison uses immutable verified baseline bytes and identity checks. -- [x] Preserve npm OIDC readiness, exact archive delivery, CDN verification, recovery and explicit treatment of uncertain performance. No publishing in this task. -- [x] Validate YAML/action pins/shell syntax and run changed automation tests. Independent review checks the agreed layout, config/CLI semantics, all-45 quality, artifact provenance and absence of tracked generated evidence. Review output stays outside Git. - -## Task 7 — Update the existing draft PR and verify hosted CI - -- [ ] Inspect the complete diff, deleted/moved files and working-tree state. Commit only source, tests, maintained Wiki, config and this active plan; never commit generated outputs. -- [ ] Push the existing branch without changing its base or rewriting preserved history. Replace PR #2 description with the simplified structure, public config/CLI contract and self-contained reproduction commands. Remove prior cross-project comparisons and obsolete evidence links. -- [ ] Run GitHub CI and inspect each required job, including the actual target comparison. Fix concrete failures; do not rerun benchmarks to select a preferred performance result. -- [ ] Link fresh CI artifacts from the PR and report exact observed test/coverage/quality/performance outcomes. Keep the PR draft. No merge, npm publication or release. - -## Completion criteria - -The current tree matches the agreed layout; all executable sources are TS; no other solver projects, copied implementations, dependency patch or generated reports remain tracked. Factory/default/custom config and CLI config are tested and documented. All five builds and required consumers pass, all 45 inputs retain mathematical correctness, and uncertainty is honestly reported. The same draft PR contains fresh hosted verification and artifact links. - -## Planning status - -The owner approved execution through subagents after confirming the two build entry points. Implementation follows this plan; generated verification evidence remains outside Git. +# Readability and delivery simplification + +The owner requested a complete readability correction across `src/` and `.github/`. +The prior directory cleanup did not make the implementation sufficiently clear. +This is the one active plan; generated measurements and review notes stay outside Git. + +## Boundaries + +Keep the flat ten-file source layout, two build entries, public tuple contract, +optional validated configuration, CLI behavior and all five distribution files. +Keep exact mathematics, deterministic command ordering, search budget semantics, +all 45 fixed inputs, strict TypeScript and every required consumer environment. +No source copies, tracked reports, extra service or new abstraction framework. + +Domain constants describe the fixed lock mechanics; they are not new config fields. +Ordinary counters and binary arithmetic do not need decorative constants. +Necessary matrix/search loops remain explicit, with short, cohesive bodies. + +## Source + +- [x] Review every source file, including small API, type and error modules. +- [x] Replace opaque internal identifiers with domain names. +- [x] Name position bounds, target, encoding units, storage byte sizes and sentinels. +- [x] Separate matrix construction, pivot operations and result classification. +- [x] Make search strategy selection, state expansion and path relaxation readable. +- [x] Keep dense/sparse predecessor storage details out of BFS traversal. +- [x] Express CLI IO and configuration validation as clear ordered operations. +- [x] Preserve all 45 exact command paths and resource-limit behavior. + +## Delivery + +- [x] Review and simplify every script under `.github/scripts/`. +- [x] Give preparation, publication and verification explicit responsibilities. +- [x] Remove compressed multi-statement lines and deeply nested delivery branches. +- [x] Preserve one verified archive, source identity, preceding-release baseline, + performance acceptance and idempotent recovery from partial publication. +- [x] Verify helpers locally before editing workflow YAML. +- [x] Use one `release.yml` workflow for PR checks, main publication and manual canary. +- [x] Consolidate verification into one readonly job, including Node 26 source tests. +- [x] Use a separate npm delivery job with fresh trusted-main preparation and OIDC; + verify npm/CDN bytes and real browser imports before the GitHub release job. +- [x] Keep GitHub write permissions separate; candidate/fork code never becomes + privileged publication tooling. +- [x] Preserve existing checks and canary behavior; do not remove features silently. + +## Verification and review + +- [x] Enforce supported Oxlint block-depth limits for source and delivery scripts. +- [x] Run strict types/lint, existing behavior/oracle/config/CLI tests and coverage. +- [x] Build and verify the same archive on Node 22/24/26 and real browser engines. +- [x] Run the 45-input benchmark after competing heavy processes stop. +- [x] Adapt and validate workflow YAML only after those local gates finish. +- [x] Independently review every source/delivery file for maintainability, not only + correctness; resolve concrete findings before updating the draft PR. +- [ ] Update the existing PR and inspect hosted CI on its exact commit. + +No merge or package publication is part of this correction. Hosted evidence and +final execution status are linked from the draft PR, rather than copied into Git. diff --git a/src/astar.ts b/src/astar.ts index a366524..9a81646 100644 --- a/src/astar.ts +++ b/src/astar.ts @@ -1,67 +1,73 @@ import { SearchLimitError } from './errors.ts'; -import { commandFor, decodeDigits, movementLimits, readAt, readByte } from './lock.ts'; -import { IndexedHeap } from './priority-queue.ts'; +import { + DIRECTIONS, + MAX_SHIFT, + commandFor, + decodeDigits, + minimumActionsForShift, + movementLimits, + readAt, + readByte, +} from './lock.ts'; +import type { Effect, LockModel, PreparedSearch } from './lock.ts'; +import { IndexedHeap, NOT_QUEUED } from './priority-queue.ts'; import type { SearchNode } from './priority-queue.ts'; -import type { LockModel, PreparedSearch } from './lock.ts'; import { analyzeMatrix } from './matrix.ts'; import { BfsSearch } from './bfs.ts'; -import { createSolverConfig } from './config.ts'; -import type { SolverConfig } from './config.ts'; +import type { SearchBudget } from './config.ts'; import type { Command } from './types.ts'; -/** One counter owner shared by the certificate, A* and singular BFS. */ -export class SearchBudget { - readonly options: SolverConfig; - private visited = 0; - private expanded = 0; - - constructor(options: SolverConfig = createSolverConfig()) { - this.options = options; - } - - check(name: keyof SolverConfig, used: number): void { - if (used > this.options[name]) throw new SearchLimitError(name, this.options[name], used); - } - - visit(): void { - this.check('maxVisited', this.visited + 1); - this.visited += 1; - } - - expand(): void { - this.check('maxExpanded', this.expanded + 1); - this.expanded += 1; +function findExecutablePlate( + prepared: PreparedSearch, + remainingShifts: readonly number[], + positionDigits: Uint8Array, +): number | null { + for (let plate = 0; plate < prepared.plateCount; plate += 1) { + const shift = readAt(remainingShifts, plate); + if (shift === 0) { + continue; + } + const effect = readAt(prepared.effects, plate); + const [positiveLimit, negativeLimit] = movementLimits(positionDigits, effect); + const allowedShift = shift > 0 ? positiveLimit : negativeLimit; + if (Math.abs(shift) <= allowedShift) { + return plate; + } } + return null; } /** - * Sufficient certificate only: legal single actions for nonzero net entries - * attain the global lower bound. Failure is discarded and full A* still runs. + * A sufficient certificate: one legal action per nonzero net shift attains the + * lower bound. Failure proves nothing; A* then starts from the initial state. */ -function greedyCertificate(prepared: PreparedSearch, net: readonly number[], budget: SearchBudget): readonly Command[] | null { - if (net.some((value) => Math.abs(value) > 6)) return null; - const remaining = [...net]; - const digits = new Uint8Array(prepared.n); - decodeDigits(prepared.source, digits); +function greedyCertificate( + prepared: PreparedSearch, + requiredShifts: readonly number[], + budget: SearchBudget, +): readonly Command[] | null { + if (requiredShifts.some((shift) => Math.abs(shift) > MAX_SHIFT)) { + return null; + } + + const remainingShifts = [...requiredShifts]; + const positionDigits = new Uint8Array(prepared.plateCount); + decodeDigits(prepared.initialCode, positionDigits); const commands: Command[] = []; - let pending = remaining.filter(Boolean).length; - while (pending > 0) { - let selected = -1; - for (let plate = 0; plate < prepared.n; plate += 1) { - const delta = readAt(remaining, plate); - if (delta === 0) continue; - const limits = movementLimits(digits, readAt(prepared.effects, plate)); - if (Math.abs(delta) <= limits[delta > 0 ? 0 : 1]) { selected = plate; break; } + let pendingActions = remainingShifts.filter((shift) => shift !== 0).length; + while (pendingActions > 0) { + const plate = findExecutablePlate(prepared, remainingShifts, positionDigits); + if (plate === null) { + return null; } - if (selected === -1) return null; - const delta = readAt(remaining, selected); - const effect = readAt(prepared.effects, selected); + const shift = readAt(remainingShifts, plate); + const effect = readAt(prepared.effects, plate); for (const { target, sign } of effect.shifts) { - digits[target] = readByte(digits, target) + delta * sign; + positionDigits[target] = readByte(positionDigits, target) + shift * sign; } - commands.push(commandFor(selected, delta)); - remaining[selected] = 0; - pending -= 1; + commands.push(commandFor(plate, shift)); + remainingShifts[plate] = 0; + pendingActions -= 1; } budget.check('maxVisited', commands.length + 1); budget.check('maxExpanded', commands.length); @@ -78,9 +84,8 @@ function reconstruct(node: SearchNode): readonly Command[] { } /** - * Invertible A gives a unique remaining net r for each physical state. An action - * changes one r[i] by at most six, so h = sum ceil(abs(r[i])/6) is consistent. - * Closed nodes are final and the first popped goal minimizes action count. + * An invertible matrix gives unique remaining shifts for each physical state. + * Their action lower bound is consistent, so the first popped goal is optimal. */ export class MatrixSearch { private readonly model: LockModel; @@ -94,72 +99,134 @@ export class MatrixSearch { } solve(): readonly Command[] | null { - const { n, source, goal, effects } = this.prepared; - const budget = this.budget; - if (source === goal) { budget.visit(); return []; } + if (this.prepared.initialCode === this.prepared.goalCode) { + this.budget.visit(); + return []; + } const analysis = analyzeMatrix(this.model); - if (analysis.kind === 'inconsistent' || analysis.kind === 'noninteger') return null; - if (analysis.kind === 'singular') return new BfsSearch(this.prepared, budget).solve(); - const certificate = greedyCertificate(this.prepared, analysis.net, budget); - if (certificate !== null) return certificate; + if (analysis.kind === 'inconsistent' || analysis.kind === 'noninteger') { + return null; + } + if (analysis.kind === 'singular') { + return new BfsSearch(this.prepared, this.budget).solve(); + } + const certificate = greedyCertificate(this.prepared, analysis.net, this.budget); + if (certificate !== null) { + return certificate; + } + return new AStarSearch(this.prepared, this.budget).solve(analysis.net, analysis.lowerBound); + } +} + +/** Mutable A* state is allocated only after the certificate cannot finish. */ +class AStarSearch { + private readonly prepared: PreparedSearch; + private readonly budget: SearchBudget; + private readonly positionDigits: Uint8Array; + private readonly frontier = new IndexedHeap(); + private readonly records = new Map(); + private nextSequence = 1; + + constructor(prepared: PreparedSearch, budget: SearchBudget) { + this.prepared = prepared; + this.budget = budget; + this.positionDigits = new Uint8Array(prepared.plateCount); + } - budget.visit(); + solve(requiredShifts: readonly number[], lowerBound: number): readonly Command[] | null { + this.budget.visit(); const root: SearchNode = { - key: source, g: 0, h: analysis.lowerBound, r: analysis.net, - previous: null, plate: -1, delta: 0, sequence: 0, heapIndex: -1, closed: false, + state: this.prepared.initialCode, + actionCount: 0, + estimate: lowerBound, + remainingShifts: requiredShifts, + previous: null, + // The root has no incoming command; reconstruction stops at previous=null. + plate: -1, + delta: 0, + sequence: 0, + heapIndex: NOT_QUEUED, + closed: false, }; - const heap = new IndexedHeap(); - heap.push(root); - const records = new Map([[source, root]]); - const digits = new Uint8Array(n); - let sequence = 1; - while (heap.size > 0) { - const node = heap.pop(); - if (node.key === goal) return reconstruct(node); - budget.expand(); + this.frontier.push(root); + this.records.set(root.state, root); + + while (this.frontier.size > 0) { + const node = this.frontier.pop(); + if (node.state === this.prepared.goalCode) { + return reconstruct(node); + } + this.budget.expand(); node.closed = true; - decodeDigits(node.key, digits); - for (let plate = 0; plate < n; plate += 1) { - const effect = effects[plate]; - const previousNet = node.r[plate]; - if (effect === undefined || previousNet === undefined) throw new Error('Internal search dimension invariant failed.'); - const limits = movementLimits(digits, effect); - const preferred = previousNet < 0 ? -1 : 1; - for (let side = 0; side < 2; side += 1) { - const sign = side === 0 ? preferred : -preferred; - const limit = limits[sign > 0 ? 0 : 1]; - for (let steps = limit; steps >= 1; steps -= 1) { - const delta = sign * steps; - const key = node.key + delta * effect.offset; - const g = node.g + 1; - const existing = records.get(key); - if (existing !== undefined && (existing.closed || existing.g <= g)) continue; - if (existing !== undefined) { - // The physical state uniquely determines r/h; finalized parent - // and a lower g are the only changes required by decrease-key. - existing.g = g; - existing.previous = node; - existing.plate = plate; - existing.delta = delta; - heap.up(existing); - continue; - } - budget.visit(); - budget.check('maxFrontier', heap.size + 1); - const r = [...node.r]; - const remaining = previousNet - delta; - r[plate] = remaining; - const h = node.h - Math.ceil(Math.abs(previousNet) / 6) + Math.ceil(Math.abs(remaining) / 6); - if (!Number.isSafeInteger(remaining) || !Number.isSafeInteger(g + h)) { - throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, String(remaining)); - } - const next: SearchNode = { key, g, h, r, previous: node, plate, delta, sequence: sequence++, heapIndex: -1, closed: false }; - records.set(key, next); - heap.push(next); - } - } + decodeDigits(node.state, this.positionDigits); + for (let plate = 0; plate < this.prepared.plateCount; plate += 1) { + this.expandPlate(node, plate); } } return null; } + + private expandPlate(node: SearchNode, plate: number): void { + const effect = readAt(this.prepared.effects, plate); + const remainingShift = readAt(node.remainingShifts, plate); + const preferredDirection = remainingShift < 0 ? -1 : 1; + const [positiveLimit, negativeLimit] = movementLimits(this.positionDigits, effect); + + // Preserve plate order, preferred direction, then larger displacement first. + for (const preference of DIRECTIONS) { + const direction = preferredDirection * preference; + const limit = direction > 0 ? positiveLimit : negativeLimit; + for (let steps = limit; steps >= 1; steps -= 1) { + this.visitNeighbor(node, plate, direction * steps, effect); + } + } + } + + private visitNeighbor(parent: SearchNode, plate: number, delta: number, effect: Effect): void { + const state = parent.state + delta * effect.stateCodeOffset; + const actionCount = parent.actionCount + 1; + const existing = this.records.get(state); + if (existing !== undefined) { + if (existing.closed || existing.actionCount <= actionCount) { + return; + } + // The physical state fixes remaining shifts and their lower bound. + // A shorter path only changes the predecessor and queue priority. + existing.actionCount = actionCount; + existing.previous = parent; + existing.plate = plate; + existing.delta = delta; + this.frontier.decreasePriority(existing); + return; + } + + this.budget.visit(); + this.budget.check('maxFrontier', this.frontier.size + 1); + const previousShift = readAt(parent.remainingShifts, plate); + const remainingShift = previousShift - delta; + const remainingShifts = [...parent.remainingShifts]; + remainingShifts[plate] = remainingShift; + const estimate = parent.estimate + - minimumActionsForShift(previousShift) + + minimumActionsForShift(remainingShift); + if (!Number.isSafeInteger(remainingShift) || !Number.isSafeInteger(actionCount + estimate)) { + throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, String(remainingShift)); + } + + const next: SearchNode = { + state, + actionCount, + estimate, + remainingShifts, + previous: parent, + plate, + delta, + sequence: this.nextSequence, + heapIndex: NOT_QUEUED, + closed: false, + }; + this.nextSequence += 1; + this.records.set(state, next); + this.frontier.push(next); + } } diff --git a/src/bfs.ts b/src/bfs.ts index ca936ed..b116d63 100644 --- a/src/bfs.ts +++ b/src/bfs.ts @@ -1,110 +1,183 @@ import { SearchLimitError } from './errors.ts'; -import { commandFor, decodeDigits, movementLimits, readAt, readByte, readInt32 } from './lock.ts'; +import { + DIRECTIONS, + MAX_SHIFT, + commandFor, + decodeDigits, + movementLimits, + readAt, + readByte, + readInt32, +} from './lock.ts'; import type { PreparedSearch } from './lock.ts'; -import type { SearchBudget } from './astar.ts'; +import type { SearchBudget } from './config.ts'; import type { Command } from './types.ts'; -interface SparseRecord { +const UNVISITED = -2; +const ROOT_PARENT = -1; +const MAX_INT32 = 0x7fffffff; +const DIRECTION_COUNT = DIRECTIONS.length; + +interface Predecessor { readonly parent: number; readonly action: number; } type Records = | { readonly dense: true; readonly parents: Int32Array; readonly actions: Uint8Array } - | { readonly dense: false; readonly parents: Map }; + | { readonly dense: false; readonly parents: Map }; + +function encodeCommand(plate: number, direction: number, steps: number): number { + const directionIndex = direction > 0 ? 0 : 1; + return (plate * DIRECTION_COUNT + directionIndex) * MAX_SHIFT + steps - 1; +} + +function decodeCommand(packed: number): Command { + const plateAndDirection = Math.floor(packed / MAX_SHIFT); + const plate = Math.floor(plateAndDirection / DIRECTION_COUNT); + const direction = readAt(DIRECTIONS, plateAndDirection % DIRECTION_COUNT); + const steps = packed % MAX_SHIFT + 1; + return commandFor(plate, direction * steps); +} + +function hasVisited(records: Records, state: number): boolean { + return records.dense + ? readInt32(records.parents, state) !== UNVISITED + : records.parents.has(state); +} -function unpack(packed: number): Command { - const plateDirection = Math.floor(packed / 6); - const sign = plateDirection % 2 === 0 ? 1 : -1; - return commandFor(Math.floor(plateDirection / 2), sign * (packed % 6 + 1)); +function remember(records: Records, state: number, parent: number, action: number): void { + if (records.dense) { + records.parents[state] = parent; + records.actions[state] = action; + } else { + records.parents.set(state, { parent, action }); + } } -function reconstruct(records: Records, source: number, code: number): readonly Command[] { +function readPredecessor(records: Records, state: number): Predecessor { + if (records.dense) { + return { + parent: readInt32(records.parents, state), + action: readByte(records.actions, state), + }; + } + const predecessor = records.parents.get(state); + if (predecessor === undefined) { + throw new Error('Internal BFS predecessor invariant failed.'); + } + return predecessor; +} + +function reconstruct(records: Records, initialCode: number, state: number): readonly Command[] { const commands: Command[] = []; - while (code !== source) { - if (records.dense) { - commands.push(unpack(readByte(records.actions, code))); - code = readInt32(records.parents, code); - } else { - const node = records.parents.get(code); - if (node === undefined) throw new Error('Internal BFS predecessor invariant failed.'); - commands.push(unpack(node.action)); - code = node.parent; - } + while (state !== initialCode) { + const predecessor = readPredecessor(records, state); + commands.push(decodeCommand(predecessor.action)); + state = predecessor.parent; } return commands.reverse(); } -/** Exact action BFS; source index, positive sign, then larger steps win ties. */ +/** Exact grouped-action BFS; plate order, positive direction, then larger shifts. */ export class BfsSearch { private readonly prepared: PreparedSearch; private readonly budget: SearchBudget; + private readonly positionDigits: Uint8Array; + private records: Records = { dense: false, parents: new Map() }; + private queue: Int32Array | number[] = []; + private queueCapacity = 1; + private readIndex = 0; + private writeIndex = 0; + private queuedStates = 0; constructor(prepared: PreparedSearch, budget: SearchBudget) { this.prepared = prepared; this.budget = budget; + this.positionDigits = new Uint8Array(prepared.plateCount); } solve(): readonly Command[] | null { - const { n, size, source, goal, effects } = this.prepared; - const budget = this.budget; - budget.visit(); - if (source === goal) return []; - - // Signed parents need two sentinel values. Queue holds only the live frontier. - const capacity = Math.min(size, budget.options.maxVisited, budget.options.maxFrontier); - const denseBytes = size * 5 + capacity * 4; - const dense = size <= 0x7fffffff && denseBytes <= budget.options.maxDenseBytes; - let records: Records; - let queue: Int32Array | number[]; + this.budget.visit(); + if (this.prepared.initialCode === this.prepared.goalCode) { + return []; + } + this.initializeStorage(); + remember(this.records, this.prepared.initialCode, ROOT_PARENT, 0); + this.enqueue(this.prepared.initialCode); + + while (this.queuedStates > 0) { + this.budget.expand(); + const state = this.dequeue(); + decodeDigits(state, this.positionDigits); + for (let plate = 0; plate < this.prepared.plateCount; plate += 1) { + if (this.expandPlate(state, plate)) { + return reconstruct(this.records, this.prepared.initialCode, this.prepared.goalCode); + } + } + } + return null; + } + + private initializeStorage(): void { + const { stateCount } = this.prepared; + const { maxVisited, maxFrontier, maxDenseBytes } = this.budget.options; + this.queueCapacity = Math.min(stateCount, maxVisited, maxFrontier); + const bytesPerRecord = Int32Array.BYTES_PER_ELEMENT + Uint8Array.BYTES_PER_ELEMENT; + const denseBytes = stateCount * bytesPerRecord + this.queueCapacity * Int32Array.BYTES_PER_ELEMENT; + // Dense parents are signed state codes; negative values are sentinels. + const useDenseStorage = stateCount <= MAX_INT32 && denseBytes <= maxDenseBytes; try { - records = dense - ? { dense: true, parents: new Int32Array(size).fill(-2), actions: new Uint8Array(size) } - : { dense: false, parents: new Map() }; - queue = dense ? new Int32Array(capacity) : []; + this.records = useDenseStorage + ? { dense: true, parents: new Int32Array(stateCount).fill(UNVISITED), actions: new Uint8Array(stateCount) } + : { dense: false, parents: new Map() }; + this.queue = useDenseStorage ? new Int32Array(this.queueCapacity) : []; } catch (error) { - if (!(error instanceof RangeError)) throw error; - throw new SearchLimitError('maxDenseBytes', budget.options.maxDenseBytes, denseBytes, { cause: error }); + if (!(error instanceof RangeError)) { + throw error; + } + throw new SearchLimitError('maxDenseBytes', maxDenseBytes, denseBytes, { cause: error }); } - if (records.dense) records.parents[source] = -1; - else records.parents.set(source, { parent: -1, action: -1 }); - queue[0] = source; - let head = 0; - let tail = 1 % capacity; - let frontier = 1; - const digits = new Uint8Array(n); - - while (frontier > 0) { - budget.expand(); - const code = queue instanceof Int32Array ? readInt32(queue, head) : readAt(queue, head); - head = (head + 1) % capacity; - frontier -= 1; - decodeDigits(code, digits); - for (let plate = 0; plate < n; plate += 1) { - const effect = effects[plate]; - if (effect === undefined) throw new Error('Internal search dimension invariant failed.'); - const limits = movementLimits(digits, effect); - for (let direction = 0; direction < 2; direction += 1) { - const sign = direction === 0 ? 1 : -1; - const limit = direction === 0 ? limits[0] : limits[1]; - for (let steps = limit; steps >= 1; steps -= 1) { - const next = code + sign * steps * effect.offset; - if (records.dense ? readInt32(records.parents, next) !== -2 : records.parents.has(next)) continue; - budget.visit(); - const action = (plate * 2 + direction) * 6 + steps - 1; - if (records.dense) { - records.parents[next] = code; - records.actions[next] = action; - } else records.parents.set(next, { parent: code, action }); - if (next === goal) return reconstruct(records, source, next); - budget.check('maxFrontier', frontier + 1); - queue[tail] = next; - tail = (tail + 1) % capacity; - frontier += 1; - } + this.readIndex = 0; + this.writeIndex = 0; + this.queuedStates = 0; + } + + /** Ring-buffer storage retains only the live frontier, including sparse mode. */ + private enqueue(state: number): void { + this.budget.check('maxFrontier', this.queuedStates + 1); + this.queue[this.writeIndex] = state; + this.writeIndex = (this.writeIndex + 1) % this.queueCapacity; + this.queuedStates += 1; + } + + private dequeue(): number { + const state = this.queue instanceof Int32Array + ? readInt32(this.queue, this.readIndex) + : readAt(this.queue, this.readIndex); + this.readIndex = (this.readIndex + 1) % this.queueCapacity; + this.queuedStates -= 1; + return state; + } + + private expandPlate(state: number, plate: number): boolean { + const effect = readAt(this.prepared.effects, plate); + const [positiveLimit, negativeLimit] = movementLimits(this.positionDigits, effect); + for (const direction of DIRECTIONS) { + const limit = direction > 0 ? positiveLimit : negativeLimit; + for (let steps = limit; steps >= 1; steps -= 1) { + const nextState = state + direction * steps * effect.stateCodeOffset; + if (hasVisited(this.records, nextState)) { + continue; + } + this.budget.visit(); + remember(this.records, nextState, state, encodeCommand(plate, direction, steps)); + if (nextState === this.prepared.goalCode) { + return true; } + this.enqueue(nextState); } } - return null; + return false; } } diff --git a/src/cli.ts b/src/cli.ts index df45b9f..dcc62c3 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -3,8 +3,9 @@ import { realpathSync } from 'node:fs'; import { readFile, writeFile } from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; import { validateSolverConfig } from './config.ts'; +import type { SolverConfig } from './config.ts'; import { createSolverConfig, solveLock } from './index.ts'; -import { LockModel } from './lock.ts'; +import { LockModel, TARGET_POSITION } from './lock.ts'; import type { Command, Position } from './types.ts'; interface TextWriter { @@ -16,6 +17,12 @@ interface Streams { readonly stderr: TextWriter; } +interface CliOptions { + readonly inputPath: string; + readonly configPath: string | null; + readonly outputPath: string | null; +} + interface CliCommand { readonly plate: number; readonly direction: 'left' | 'right'; @@ -33,27 +40,38 @@ type CliResult = ResultBase & ( | { readonly status: 'solved'; readonly finalState: readonly Position[]; - readonly metrics: { readonly commands: number; readonly divisions: number; readonly plateSwitches: number }; + readonly metrics: { + readonly commands: number; + readonly divisions: number; + readonly plateSwitches: number; + }; } ); -const usage = 'Использование: gothic-lock-solver [--config ] [--output ]\n gothic-lock-solver --help'; +const USAGE = 'Использование: gothic-lock-solver [--config ] [--output ]' + + '\n gothic-lock-solver --help'; -function parseArgs(argv: readonly string[]): { inputPath: string; configPath: string | null; outputPath: string | null } { +function parseArgs(argv: readonly string[]): CliOptions { const inputPath = argv[0]; - if (typeof inputPath !== 'string' || inputPath.length === 0 || inputPath.startsWith('--')) throw new Error(usage); + if (typeof inputPath !== 'string' || inputPath.length === 0 || inputPath.startsWith('--')) { + throw new Error(USAGE); + } + let configPath: string | null = null; let outputPath: string | null = null; + // Each optional flag consumes its following file path. for (let index = 1; index < argv.length; index += 2) { const flag = argv[index]; const path = argv[index + 1]; - if (typeof path !== 'string' || path.length === 0 || path.startsWith('--')) throw new Error(usage); + if (typeof path !== 'string' || path.length === 0 || path.startsWith('--')) { + throw new Error(USAGE); + } if (flag === '--config' && configPath === null) { configPath = path; } else if (flag === '--output' && outputPath === null) { outputPath = path; } else { - throw new Error(usage); + throw new Error(USAGE); } } return { inputPath, configPath, outputPath }; @@ -67,7 +85,8 @@ function parseJson(json: string): unknown { } } -function parseInput(json: string): LockModel { +async function readLock(path: string): Promise { + const json = await readFile(path, 'utf8'); const value = parseJson(json); if (typeof value !== 'object' || value === null || Array.isArray(value) || !('state' in value) || !('links' in value)) { throw new Error('Ожидается объект с полями state и links.'); @@ -75,32 +94,56 @@ function parseInput(json: string): LockModel { return new LockModel(value.state, value.links); } +async function readConfig(path: string | null): Promise { + if (path === null) { + return createSolverConfig(); + } + const json = await readFile(path, 'utf8'); + return validateSolverConfig(parseJson(json), 'overrides'); +} + /** CLI files use one-based plates and physical-direction notation. */ function createResult(state: readonly Position[], solution: readonly Command[] | null): CliResult { const initialState = [...state]; - const targetState = state.map((): Position => 4); - if (solution === null) return { status: 'unsolvable', initialState, targetState, commands: [] }; - let previous: number | undefined; + const targetState = state.map((): Position => TARGET_POSITION); + if (solution === null) { + return { status: 'unsolvable', initialState, targetState, commands: [] }; + } + + let previousPlate: number | undefined; let divisions = 0; let plateSwitches = 0; const commands: CliCommand[] = []; - for (const [index, delta] of solution) { - if (previous !== undefined && previous !== index) plateSwitches += 1; - previous = index; - divisions += Math.abs(delta); - commands.push({ plate: index + 1, direction: delta > 0 ? 'left' : 'right', steps: Math.abs(delta) }); + for (const [plate, delta] of solution) { + if (previousPlate !== undefined && previousPlate !== plate) { + plateSwitches += 1; + } + previousPlate = plate; + const steps = Math.abs(delta); + divisions += steps; + commands.push({ plate: plate + 1, direction: delta > 0 ? 'left' : 'right', steps }); } return { - status: 'solved', initialState, targetState, commands, finalState: [...targetState], + status: 'solved', + initialState, + targetState, + commands, + finalState: [...targetState], metrics: { commands: commands.length, divisions, plateSwitches }, }; } function formatResult(result: CliResult): string { - if (result.status === 'unsolvable') return 'Решение не найдено.'; - if (result.commands.length === 0) return 'Замок уже открыт.'; - const lines = result.commands.map((command, index) => - `${index + 1}. Пластина ${command.plate} - ${command.direction === 'left' ? 'влево' : 'вправо'} x${command.steps}`); + if (result.status === 'unsolvable') { + return 'Решение не найдено.'; + } + if (result.commands.length === 0) { + return 'Замок уже открыт.'; + } + const lines = result.commands.map((command, index) => { + const direction = command.direction === 'left' ? 'влево' : 'вправо'; + return `${index + 1}. Пластина ${command.plate} - ${direction} x${command.steps}`; + }); return [`Найдено команд: ${result.commands.length}`, ...lines].join('\n'); } @@ -108,27 +151,31 @@ function formatResult(result: CliResult): string { export async function runCli(argv: readonly string[], streams: Streams = process): Promise<0 | 1 | 2> { try { if (argv.length === 1 && argv[0] === '--help') { - streams.stdout.write(`${usage}\n`); + streams.stdout.write(`${USAGE}\n`); return 0; } const { inputPath, configPath, outputPath } = parseArgs(argv); - const model = parseInput(await readFile(inputPath, 'utf8')); - const config = configPath === null - ? createSolverConfig() - : createSolverConfig(validateSolverConfig(parseJson(await readFile(configPath, 'utf8')), true)); - const result = createResult(model.state, solveLock(model.state, model.links, config)); - if (outputPath !== null) await writeFile(outputPath, `${JSON.stringify(result, null, 2)}\n`, 'utf8'); + const model = await readLock(inputPath); + const config = await readConfig(configPath); + const solution = solveLock(model.state, model.links, config); + const result = createResult(model.state, solution); + if (outputPath !== null) { + await writeFile(outputPath, `${JSON.stringify(result, null, 2)}\n`, 'utf8'); + } streams.stdout.write(`${formatResult(result)}\n`); return result.status === 'unsolvable' ? 2 : 0; } catch (error) { - streams.stderr.write(`Ошибка: ${error instanceof Error ? error.message : String(error)}\n`); + const message = error instanceof Error ? error.message : String(error); + streams.stderr.write(`Ошибка: ${message}\n`); return 1; } } function isEntryPoint(): boolean { const entryPath = process.argv[1]; - if (entryPath === undefined) return false; + if (entryPath === undefined) { + return false; + } try { return realpathSync(entryPath) === realpathSync(fileURLToPath(import.meta.url)); } catch { @@ -136,4 +183,6 @@ function isEntryPoint(): boolean { } } -if (isEntryPoint()) process.exitCode = await runCli(process.argv.slice(2)); +if (isEntryPoint()) { + process.exitCode = await runCli(process.argv.slice(2)); +} diff --git a/src/config.ts b/src/config.ts index f8676ef..3d4eb79 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,4 @@ -import { LockInputError } from './errors.ts'; +import { LockInputError, SearchLimitError } from './errors.ts'; /** Per-solve resource limits. Byte and expansion limits may be zero. */ export interface SolverConfig { @@ -15,18 +15,26 @@ const DEFAULTS: SolverConfig = Object.freeze({ maxDenseBytes: 64 * 1024 * 1024, }); +type ConfigMode = 'complete' | 'overrides'; + function isConfigField(key: PropertyKey): key is keyof SolverConfig { - return key === 'maxVisited' || key === 'maxExpanded' || key === 'maxFrontier' || key === 'maxDenseBytes'; + return key === 'maxVisited' + || key === 'maxExpanded' + || key === 'maxFrontier' + || key === 'maxDenseBytes'; } -/** Shared runtime boundary for typed callers, ordinary JavaScript and CLI JSON. */ -export function validateSolverConfig(value: unknown, partial = false): SolverConfig { +/** Shared factory implementation for typed overrides, complete configs and JSON. */ +export function validateSolverConfig(value: unknown, mode: ConfigMode = 'complete'): SolverConfig { if (typeof value !== 'object' || value === null || Array.isArray(value)) { throw new LockInputError('Конфигурация поиска должна быть объектом.'); } + const config = { ...DEFAULTS }; for (const key of Reflect.ownKeys(value)) { - if (!isConfigField(key)) throw new LockInputError(`Неизвестное поле конфигурации: ${String(key)}.`); + if (!isConfigField(key)) { + throw new LockInputError(`Неизвестное поле конфигурации: ${String(key)}.`); + } const setting: unknown = Reflect.get(value, key); const minimum = key === 'maxVisited' || key === 'maxFrontier' ? 1 : 0; if (typeof setting !== 'number' || !Number.isSafeInteger(setting) || setting < minimum) { @@ -34,15 +42,45 @@ export function validateSolverConfig(value: unknown, partial = false): SolverCon } config[key] = setting; } - if (!partial) { + if (mode === 'complete') { for (const key of Object.keys(DEFAULTS)) { - if (!Object.hasOwn(value, key)) throw new LockInputError(`В конфигурации отсутствует поле ${key}.`); + if (!Object.hasOwn(value, key)) { + throw new LockInputError(`В конфигурации отсутствует поле ${key}.`); + } } } return Object.freeze(config); } -/** Return a complete immutable configuration without changing the overrides. */ +/** Return complete immutable settings without changing the caller's overrides. */ export function createSolverConfig(overrides: Partial = {}): SolverConfig { - return validateSolverConfig(overrides, true); + return validateSolverConfig(overrides, 'overrides'); +} + +/** Shared consumption counters for the certificate and both search algorithms. */ +export class SearchBudget { + readonly options: SolverConfig; + private visitedStates = 0; + private expandedStates = 0; + + constructor(options: SolverConfig = createSolverConfig()) { + this.options = options; + } + + check(name: keyof SolverConfig, used: number): void { + const maximum = this.options[name]; + if (used > maximum) { + throw new SearchLimitError(name, maximum, used); + } + } + + visit(): void { + this.check('maxVisited', this.visitedStates + 1); + this.visitedStates += 1; + } + + expand(): void { + this.check('maxExpanded', this.expandedStates + 1); + this.expandedStates += 1; + } } diff --git a/src/errors.ts b/src/errors.ts index e06083e..5da13a2 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -6,7 +6,13 @@ export class LockInputError extends Error { } } -export type SearchLimit = 'maxVisited' | 'maxExpanded' | 'maxFrontier' | 'maxDenseBytes' | 'stateEncoding' | 'matrixArithmetic'; +export type SearchLimit = + | 'maxVisited' + | 'maxExpanded' + | 'maxFrontier' + | 'maxDenseBytes' + | 'stateEncoding' + | 'matrixArithmetic'; /** Computation exhausted a resource; this is never proof of unreachability. */ export class SearchLimitError extends Error { @@ -14,7 +20,12 @@ export class SearchLimitError extends Error { readonly maximum: number; readonly used: number | string; - constructor(limit: SearchLimit, maximum: number, used: number | string, options?: ErrorOptions) { + constructor( + limit: SearchLimit, + maximum: number, + used: number | string, + options?: ErrorOptions, + ) { super(`Превышен ресурсный предел поиска: ${limit} (предел ${maximum}, требуется ${used}).`, options); this.name = 'SearchLimitError'; this.limit = limit; diff --git a/src/index.ts b/src/index.ts index c5ff16c..7c23b87 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ import { LockModel, PreparedSearch } from './lock.ts'; -import { MatrixSearch, SearchBudget } from './astar.ts'; -import { createSolverConfig, validateSolverConfig } from './config.ts'; +import { MatrixSearch } from './astar.ts'; +import { SearchBudget, createSolverConfig, validateSolverConfig } from './config.ts'; import type { SolverConfig } from './config.ts'; -import type { Command, Links, Position, State } from './types.ts'; +import type { Command, Links, Position, StateConstraint } from './types.ts'; /** * Find a legal sequence with the minimum number of actions, or null when the @@ -16,14 +16,15 @@ import type { Command, Links, Position, State } from './types.ts'; * @throws {SearchLimitError} When a computation limit is exceeded (not unsolvable). */ export function solveLock( - state: S & (number extends S['length'] ? unknown : S extends State ? unknown : never), + state: S & StateConstraint, links: NoInfer>, config?: SolverConfig, ): readonly Command[] | null { const options = config === undefined ? createSolverConfig() : validateSolverConfig(config); const model = new LockModel(state, links); const prepared = new PreparedSearch(model); - return new MatrixSearch(model, prepared, new SearchBudget(options)).solve(); + const budget = new SearchBudget(options); + return new MatrixSearch(model, prepared, budget).solve(); } export { createSolverConfig } from './config.ts'; diff --git a/src/lock.ts b/src/lock.ts index 47812a2..ccb6f96 100644 --- a/src/lock.ts +++ b/src/lock.ts @@ -1,163 +1,206 @@ import { LockInputError, SearchLimitError } from './errors.ts'; import type { Command, Delta, Link, Position } from './types.ts'; +export const MIN_POSITION = 1; +export const MAX_POSITION = 7; +export const TARGET_POSITION = 4; +export const POSITION_COUNT = MAX_POSITION - MIN_POSITION + 1; +export const MAX_SHIFT = MAX_POSITION - MIN_POSITION; +export const DIRECTIONS = [1, -1] as const; +const MIN_PLATES = 2; + function isArray(value: unknown): value is readonly unknown[] { return Array.isArray(value); } function isPosition(value: unknown): value is Position { - return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 7; + return typeof value === 'number' + && Number.isInteger(value) + && value >= MIN_POSITION + && value <= MAX_POSITION; } function isLink(value: unknown): value is Link { return value === -1 || value === 0 || value === 1; } -/** Owns a validated immutable copy of the lock, independent of caller mutations. */ -export class LockModel { - readonly state: readonly Position[]; - readonly links: readonly (readonly Link[])[]; +function copyState(state: unknown): readonly Position[] { + if (!isArray(state) || state.length < MIN_PLATES) { + throw new LockInputError(`state должен содержать не менее ${MIN_PLATES} пластин.`); + } - constructor(state: unknown, links: unknown) { - if (!isArray(state) || state.length < 2) { - throw new LockInputError('state должен содержать не менее 2 пластин.'); + const positions: Position[] = []; + for (let plate = 0; plate < state.length; plate += 1) { + const position = state[plate]; + if (!Object.hasOwn(state, plate) || !isPosition(position)) { + throw new LockInputError(`state[${plate}] должен быть целым числом от ${MIN_POSITION} до ${MAX_POSITION}.`); } - const copiedState: Position[] = []; - for (let index = 0; index < state.length; index += 1) { - const position = state[index]; - if (!Object.hasOwn(state, index) || !isPosition(position)) { - throw new LockInputError(`state[${index}] должен быть целым числом от 1 до 7.`); - } - copiedState.push(position); + positions.push(position); + } + return Object.freeze(positions); +} + +function copyLinkRow(row: unknown, source: number, plateCount: number): readonly Link[] { + if (!isArray(row) || row.length !== plateCount) { + throw new LockInputError(`links[${source}] должен содержать ${plateCount} элементов.`); + } + + const coefficients: Link[] = []; + for (let target = 0; target < plateCount; target += 1) { + const coefficient = row[target]; + if (!Object.hasOwn(row, target) || !isLink(coefficient)) { + throw new LockInputError(`links[${source}][${target}] должен быть -1, 0 или 1.`); } - if (!isArray(links) || links.length !== state.length) { - throw new LockInputError(`links должен быть матрицей ${state.length}x${state.length}.`); + if (source === target && coefficient !== 0) { + throw new LockInputError(`links[${source}][${target}] на диагонали должен быть 0.`); } - const copiedLinks: (readonly Link[])[] = []; - for (let source = 0; source < links.length; source += 1) { - const row = links[source]; - if (!Object.hasOwn(links, source) || !isArray(row) || row.length !== state.length) { - throw new LockInputError(`links[${source}] должен содержать ${state.length} элементов.`); - } - const copiedRow: Link[] = []; - for (let target = 0; target < row.length; target += 1) { - const coefficient = row[target]; - if (!Object.hasOwn(row, target) || !isLink(coefficient)) { - throw new LockInputError(`links[${source}][${target}] должен быть -1, 0 или 1.`); - } - if (source === target && coefficient !== 0) { - throw new LockInputError(`links[${source}][${target}] на диагонали должен быть 0.`); - } - copiedRow.push(coefficient); - } - copiedLinks.push(Object.freeze(copiedRow)); + coefficients.push(coefficient); + } + return Object.freeze(coefficients); +} + +function copyLinks(links: unknown, plateCount: number): readonly (readonly Link[])[] { + if (!isArray(links) || links.length !== plateCount) { + throw new LockInputError(`links должен быть матрицей ${plateCount}x${plateCount}.`); + } + + const rows: (readonly Link[])[] = []; + for (let source = 0; source < plateCount; source += 1) { + if (!Object.hasOwn(links, source)) { + throw new LockInputError(`links[${source}] должен содержать ${plateCount} элементов.`); } - this.state = Object.freeze(copiedState); - this.links = Object.freeze(copiedLinks); + rows.push(copyLinkRow(links[source], source, plateCount)); + } + return Object.freeze(rows); +} + +/** Owns validated immutable inputs, independent of caller mutations. */ +export class LockModel { + readonly state: readonly Position[]; + readonly links: readonly (readonly Link[])[]; + + constructor(state: unknown, links: unknown) { + this.state = copyState(state); + this.links = copyLinks(links, this.state.length); } } export interface Effect { readonly shifts: readonly { readonly target: number; readonly sign: number }[]; - readonly offset: number; + readonly stateCodeOffset: number; } -/** Base-7 encoding and precomputed direct effects belong to one search instance. */ +/** Base-7 state codes and direct plate effects prepared once per search. */ export class PreparedSearch { - readonly n: number; - readonly size: number; - readonly source: number; - readonly goal: number; + readonly plateCount: number; + readonly stateCount: number; + readonly initialCode: number; + readonly goalCode: number; readonly effects: readonly Effect[]; - constructor(model: LockModel) { - const { state, links } = model; - const n = state.length; - const size = 7 ** n; - // Check before matrix work and even the already-open shortcut. N=18 is safe. - if (!Number.isSafeInteger(size)) { - throw new SearchLimitError('stateEncoding', Number.MAX_SAFE_INTEGER, size); + constructor({ state, links }: LockModel) { + this.plateCount = state.length; + this.stateCount = POSITION_COUNT ** this.plateCount; + // Enforce the representation limit before even the already-open shortcut. + if (!Number.isSafeInteger(this.stateCount)) { + throw new SearchLimitError('stateEncoding', Number.MAX_SAFE_INTEGER, this.stateCount); } - const powers = Array.from({ length: n }, (_, index) => 7 ** (n - index - 1)); - let source = 0; - let goal = 0; - for (let index = 0; index < n; index += 1) { - const power = readAt(powers, index); - source += (readAt(state, index) - 1) * power; - goal += 3 * power; + + const placeValues = Array.from( + { length: this.plateCount }, + (_, plate) => POSITION_COUNT ** (this.plateCount - plate - 1), + ); + let initialCode = 0; + let goalCode = 0; + for (let plate = 0; plate < this.plateCount; plate += 1) { + const placeValue = readAt(placeValues, plate); + initialCode += (readAt(state, plate) - MIN_POSITION) * placeValue; + goalCode += (TARGET_POSITION - MIN_POSITION) * placeValue; } - this.n = n; - this.size = size; - this.source = source; - this.goal = goal; - this.effects = links.map((row, plate) => { - const shifts: { target: number; sign: number }[] = []; - let offset = 0; - for (let target = 0; target < n; target += 1) { - const sign = plate === target ? 1 : readAt(row, target); - if (sign === 0) continue; - shifts.push({ target, sign }); - offset += sign * readAt(powers, target); - } - return { shifts, offset }; - }); + this.initialCode = initialCode; + this.goalCode = goalCode; + this.effects = links.map((row, plate) => prepareEffect(row, plate, placeValues)); } } -export function decodeDigits(code: number, digits: Uint8Array): void { - for (let index = digits.length - 1; index >= 0; index -= 1) { - digits[index] = code % 7; - code = Math.floor(code / 7); +function prepareEffect(row: readonly Link[], plate: number, placeValues: readonly number[]): Effect { + const shifts: { target: number; sign: number }[] = []; + let stateCodeOffset = 0; + for (let target = 0; target < row.length; target += 1) { + const sign = plate === target ? 1 : readAt(row, target); + if (sign === 0) { + continue; + } + shifts.push({ target, sign }); + stateCodeOffset += sign * readAt(placeValues, target); } + return { shifts, stateCodeOffset }; } -/** Monotone direct motion makes these endpoint bounds valid for every unit step. */ -export function movementLimits(digits: Uint8Array, effect: Effect): readonly [number, number] { - let plus = 6; - let minus = 6; +export function decodeDigits(stateCode: number, digits: Uint8Array): void { + for (let plate = digits.length - 1; plate >= 0; plate -= 1) { + digits[plate] = stateCode % POSITION_COUNT; + stateCode = Math.floor(stateCode / POSITION_COUNT); + } +} + +/** Monotone direct motion makes endpoint bounds sufficient for every unit step. */ +export function movementLimits(digits: Uint8Array, effect: Effect): readonly [positive: number, negative: number] { + let positiveLimit = MAX_SHIFT; + let negativeLimit = MAX_SHIFT; for (const { target, sign } of effect.shifts) { - const position = readByte(digits, target); + const positionDigit = readByte(digits, target); + const distanceToStart = positionDigit; + const distanceToEnd = MAX_SHIFT - positionDigit; if (sign === 1) { - if (6 - position < plus) plus = 6 - position; - if (position < minus) minus = position; + positiveLimit = Math.min(positiveLimit, distanceToEnd); + negativeLimit = Math.min(negativeLimit, distanceToStart); } else { - if (position < plus) plus = position; - if (6 - position < minus) minus = 6 - position; + positiveLimit = Math.min(positiveLimit, distanceToStart); + negativeLimit = Math.min(negativeLimit, distanceToEnd); } } - return [plus, minus]; + return [positiveLimit, negativeLimit]; +} + +/** One grouped action can contribute at most MAX_SHIFT positions on one plate. */ +export function minimumActionsForShift(shift: number): number { + return Math.ceil(Math.abs(shift) / MAX_SHIFT); } function isDelta(value: number): value is Delta { - return Number.isInteger(value) && value !== 0 && value >= -6 && value <= 6; + return Number.isInteger(value) && value !== 0 && value >= -MAX_SHIFT && value <= MAX_SHIFT; } export function commandFor(plate: number, delta: number): Command { - if (!isDelta(delta)) throw new Error('Internal command displacement invariant failed.'); + if (!isDelta(delta)) { + throw new Error('Internal command displacement invariant failed.'); + } return [plate, delta]; } -/** - * Checked indexed access for internally dense arrays and bounded state tables. - * Call sites establish bounds through validated dimensions, allocated lengths or - * search membership. The check keeps that invariant executable and avoids type - * assertions / unchecked non-null accesses under noUncheckedIndexedAccess. - */ +/** Checked access keeps dense-array invariants executable under strict indexing. */ export function readAt(values: readonly T[], index: number): T { const value = values[index]; - if (value === undefined) throw new Error(`Internal indexed-access invariant failed at ${index}.`); + if (value === undefined) { + throw new Error(`Internal indexed-access invariant failed at ${index}.`); + } return value; } /** Typed readers keep hot typed-array access separate from object-array shapes. */ export function readByte(values: Uint8Array, index: number): number { const value = values[index]; - if (value === undefined) throw new Error(`Internal byte-access invariant failed at ${index}.`); + if (value === undefined) { + throw new Error(`Internal byte-access invariant failed at ${index}.`); + } return value; } export function readInt32(values: Int32Array, index: number): number { const value = values[index]; - if (value === undefined) throw new Error(`Internal integer-access invariant failed at ${index}.`); + if (value === undefined) { + throw new Error(`Internal integer-access invariant failed at ${index}.`); + } return value; } diff --git a/src/matrix.ts b/src/matrix.ts index 20a2774..f84ca9b 100644 --- a/src/matrix.ts +++ b/src/matrix.ts @@ -1,81 +1,159 @@ import { SearchLimitError } from './errors.ts'; -import { readAt } from './lock.ts'; +import { TARGET_POSITION, minimumActionsForShift, readAt } from './lock.ts'; import type { LockModel } from './lock.ts'; interface Rational { - readonly n: bigint; - readonly d: bigint; + readonly numerator: bigint; + readonly denominator: bigint; } -const abs = (value: bigint): bigint => value < 0n ? -value : value; - -/** Canonical exact rational: coprime numerator/denominator, positive denominator. */ -function fraction(n: bigint, d: bigint = 1n): Rational { - if (d === 0n) throw new Error('Internal zero-denominator invariant failed.'); - if (n === 0n) return { n: 0n, d: 1n }; - if (d < 0n) { n = -n; d = -d; } - let a = abs(n); - let b = d; - while (b !== 0n) [a, b] = [b, a % b]; - return { n: n / a, d: d / a }; +function absolute(value: bigint): bigint { + return value < 0n ? -value : value; } -const divide = (a: Rational, b: Rational): Rational => fraction(a.n * b.d, a.d * b.n); -const subtractProduct = (a: Rational, b: Rational, c: Rational): Rational => - fraction(a.n * b.d * c.d - b.n * c.n * a.d, a.d * b.d * c.d); +function greatestCommonDivisor(left: bigint, right: bigint): bigint { + while (right !== 0n) { + const remainder = left % right; + left = right; + right = remainder; + } + return left; +} + +/** Coprime numerator and denominator, with a positive denominator. */ +function fraction(numerator: bigint, denominator: bigint = 1n): Rational { + if (denominator === 0n) { + throw new Error('Internal zero-denominator invariant failed.'); + } + if (numerator === 0n) { + return { numerator: 0n, denominator: 1n }; + } + if (denominator < 0n) { + numerator = -numerator; + denominator = -denominator; + } + const divisor = greatestCommonDivisor(absolute(numerator), denominator); + return { numerator: numerator / divisor, denominator: denominator / divisor }; +} + +function divide(value: Rational, divisor: Rational): Rational { + return fraction( + value.numerator * divisor.denominator, + value.denominator * divisor.numerator, + ); +} + +function subtractProduct(value: Rational, factor: Rational, pivotValue: Rational): Rational { + const denominator = value.denominator * factor.denominator * pivotValue.denominator; + const scaledValue = value.numerator * factor.denominator * pivotValue.denominator; + const scaledProduct = factor.numerator * pivotValue.numerator * value.denominator; + return fraction(scaledValue - scaledProduct, denominator); +} export type MatrixAnalysis = | { readonly kind: 'inconsistent'; readonly rank: number } | { readonly kind: 'singular'; readonly rank: number } | { readonly kind: 'noninteger'; readonly rank: number } - | { readonly kind: 'unique-integer'; readonly rank: number; readonly net: readonly number[]; readonly lowerBound: number }; + | { + readonly kind: 'unique-integer'; + readonly rank: number; + readonly net: readonly number[]; + readonly lowerBound: number; + }; + +/** Transpose source-first links, add self-motion, then append goal minus state. */ +function createAugmentedMatrix({ state, links }: LockModel): Rational[][] { + const rows: Rational[][] = []; + for (let target = 0; target < state.length; target += 1) { + const row: Rational[] = []; + for (let source = 0; source < state.length; source += 1) { + const coefficient = source === target ? 1 : readAt(readAt(links, source), target); + row.push(fraction(BigInt(coefficient))); + } + row.push(fraction(BigInt(TARGET_POSITION - readAt(state, target)))); + rows.push(row); + } + return rows; +} + +function findPivotRow(rows: readonly Rational[][], column: number, firstRow: number): number | null { + for (let row = firstRow; row < rows.length; row += 1) { + if (readAt(readAt(rows, row), column).numerator !== 0n) { + return row; + } + } + return null; +} + +function normalizePivotRow(row: Rational[], pivotColumn: number): void { + const divisor = readAt(row, pivotColumn); + for (let column = pivotColumn; column < row.length; column += 1) { + row[column] = divide(readAt(row, column), divisor); + } +} + +function eliminatePivotColumn(rows: Rational[][], pivotRowIndex: number, pivotColumn: number): void { + const pivotRow = readAt(rows, pivotRowIndex); + for (let rowIndex = 0; rowIndex < rows.length; rowIndex += 1) { + const row = readAt(rows, rowIndex); + const factor = readAt(row, pivotColumn); + if (rowIndex === pivotRowIndex || factor.numerator === 0n) { + continue; + } + for (let column = pivotColumn; column < row.length; column += 1) { + row[column] = subtractProduct(readAt(row, column), factor, readAt(pivotRow, column)); + } + } +} + +function toSafeInteger({ numerator }: Rational): number { + if (absolute(numerator) > BigInt(Number.MAX_SAFE_INTEGER)) { + throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, String(numerator)); + } + return Number(numerator); +} /** - * Exact necessary equation A z = goal - state. links[source][target] is - * transposed into A[target][source]; selected-pin motion gives the diagonal 1. - * Rational inconsistency or fractional unique z proves no integer path exists. + * Solve the exact necessary equation A z = goal - state using Gauss–Jordan. + * Inconsistency or a fractional unique z proves no integer path exists. * Rank deficiency alone says nothing about reachability and requires exact BFS. */ -export function analyzeMatrix({ state, links }: LockModel): MatrixAnalysis { - const n = state.length; - const rows = Array.from({ length: n }, (_, target) => [ - ...Array.from({ length: n }, (_, source) => fraction(BigInt(source === target ? 1 : readAt(readAt(links, source), target)))), - fraction(BigInt(4 - readAt(state, target))), - ]); - const columns: number[] = []; +export function analyzeMatrix(model: LockModel): MatrixAnalysis { + const rows = createAugmentedMatrix(model); + const plateCount = model.state.length; let rank = 0; - for (let column = 0; column < n; column += 1) { - let pivot = rank; - while (pivot < n && readAt(readAt(rows, pivot), column).n === 0n) pivot += 1; - if (pivot === n) continue; + + for (let column = 0; column < plateCount; column += 1) { + const pivot = findPivotRow(rows, column, rank); + if (pivot === null) { + continue; + } const pivotRow = readAt(rows, pivot); rows[pivot] = readAt(rows, rank); rows[rank] = pivotRow; - const divisor = readAt(pivotRow, column); - for (let j = column; j <= n; j += 1) pivotRow[j] = divide(readAt(pivotRow, j), divisor); - for (let row = 0; row < n; row += 1) { - const values = readAt(rows, row); - const factor = readAt(values, column); - if (row === rank || factor.n === 0n) continue; - for (let j = column; j <= n; j += 1) values[j] = subtractProduct(readAt(values, j), factor, readAt(pivotRow, j)); - } - columns.push(column); + normalizePivotRow(pivotRow, column); + eliminatePivotColumn(rows, rank, column); rank += 1; } - for (let row = rank; row < n; row += 1) { - if (readAt(readAt(rows, row), n).n !== 0n) return { kind: 'inconsistent', rank }; - } - if (rank < n) return { kind: 'singular', rank }; - const exact: Rational[] = Array.from({ length: n }, () => fraction(0n)); - for (let row = 0; row < n; row += 1) exact[readAt(columns, row)] = readAt(readAt(rows, row), n); - if (exact.some((value) => value.d !== 1n)) return { kind: 'noninteger', rank }; - const net = exact.map(({ n: numerator }) => { - if (abs(numerator) > BigInt(Number.MAX_SAFE_INTEGER)) { - throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, String(numerator)); + + for (let row = rank; row < plateCount; row += 1) { + const rightHandSide = readAt(readAt(rows, row), plateCount); + if (rightHandSide.numerator !== 0n) { + return { kind: 'inconsistent', rank }; } - return Number(numerator); - }); - const lowerBound = net.reduce((sum, value) => sum + Math.ceil(Math.abs(value) / 6), 0); + } + if (rank < plateCount) { + return { kind: 'singular', rank }; + } + + // A full-rank square system has one pivot per column, in plate order. + const exactShifts = rows.map((row) => readAt(row, plateCount)); + if (exactShifts.some(({ denominator }) => denominator !== 1n)) { + return { kind: 'noninteger', rank }; + } + + const net = exactShifts.map(toSafeInteger); + const lowerBound = net.reduce((sum, shift) => sum + minimumActionsForShift(shift), 0); if (!Number.isSafeInteger(lowerBound)) { throw new SearchLimitError('matrixArithmetic', Number.MAX_SAFE_INTEGER, lowerBound); } diff --git a/src/priority-queue.ts b/src/priority-queue.ts index 8a4baab..dd539b6 100644 --- a/src/priority-queue.ts +++ b/src/priority-queue.ts @@ -1,8 +1,10 @@ +export const NOT_QUEUED = -1; + export interface SearchNode { - readonly key: number; - g: number; - readonly h: number; - readonly r: readonly number[]; + readonly state: number; + actionCount: number; + readonly estimate: number; + readonly remainingShifts: readonly number[]; previous: SearchNode | null; plate: number; delta: number; @@ -11,27 +13,40 @@ export interface SearchNode { closed: boolean; } -/** One entry per open state; decrease-key never leaves obsolete heap nodes. */ +/** One entry per open state; decreasing priority never leaves obsolete nodes. */ export class IndexedHeap { private readonly nodes: SearchNode[] = []; - get size(): number { return this.nodes.length; } + get size(): number { + return this.nodes.length; + } - private before(a: SearchNode, b: SearchNode): boolean { - return a.g + a.h < b.g + b.h || - (a.g + a.h === b.g + b.h && (a.h < b.h || (a.h === b.h && a.sequence < b.sequence))); + private hasHigherPriority(left: SearchNode, right: SearchNode): boolean { + const leftTotal = left.actionCount + left.estimate; + const rightTotal = right.actionCount + right.estimate; + if (leftTotal !== rightTotal) { + return leftTotal < rightTotal; + } + if (left.estimate !== right.estimate) { + return left.estimate < right.estimate; + } + return left.sequence < right.sequence; } - up(node: SearchNode): void { + decreasePriority(node: SearchNode): void { let index = node.heapIndex; while (index > 0) { - const parent = Math.floor((index - 1) / 2); - const parentNode = this.nodes[parent]; - if (parentNode === undefined) throw new Error('Internal heap parent invariant failed.'); - if (!this.before(node, parentNode)) break; - this.nodes[index] = parentNode; - parentNode.heapIndex = index; - index = parent; + const parentIndex = Math.floor((index - 1) / 2); + const parent = this.nodes[parentIndex]; + if (parent === undefined) { + throw new Error('Internal heap parent invariant failed.'); + } + if (!this.hasHigherPriority(node, parent)) { + break; + } + this.nodes[index] = parent; + parent.heapIndex = index; + index = parentIndex; } this.nodes[index] = node; node.heapIndex = index; @@ -40,31 +55,46 @@ export class IndexedHeap { push(node: SearchNode): void { node.heapIndex = this.nodes.length; this.nodes.push(node); - this.up(node); + this.decreasePriority(node); } pop(): SearchNode { const first = this.nodes[0]; - if (first === undefined) throw new Error('Internal empty-heap invariant failed.'); const last = this.nodes.pop(); - if (last === undefined) throw new Error('Internal empty-heap invariant failed.'); - first.heapIndex = -1; - if (this.nodes.length === 0) return first; + if (first === undefined || last === undefined) { + throw new Error('Internal empty-heap invariant failed.'); + } + first.heapIndex = NOT_QUEUED; + if (this.nodes.length === 0) { + return first; + } + let index = 0; while (true) { - let child = index * 2 + 1; - if (child >= this.nodes.length) break; - let childNode = this.nodes[child]; - if (childNode === undefined) throw new Error('Internal heap child invariant failed.'); - if (child + 1 < this.nodes.length) { - const right = this.nodes[child + 1]; - if (right === undefined) throw new Error('Internal heap child invariant failed.'); - if (this.before(right, childNode)) { child += 1; childNode = right; } + let childIndex = index * 2 + 1; + if (childIndex >= this.nodes.length) { + break; + } + let child = this.nodes[childIndex]; + if (child === undefined) { + throw new Error('Internal heap child invariant failed.'); + } + if (childIndex + 1 < this.nodes.length) { + const rightChild = this.nodes[childIndex + 1]; + if (rightChild === undefined) { + throw new Error('Internal heap child invariant failed.'); + } + if (this.hasHigherPriority(rightChild, child)) { + childIndex += 1; + child = rightChild; + } + } + if (!this.hasHigherPriority(child, last)) { + break; } - if (!this.before(childNode, last)) break; - this.nodes[index] = childNode; - childNode.heapIndex = index; - index = child; + this.nodes[index] = child; + child.heapIndex = index; + index = childIndex; } this.nodes[index] = last; last.heapIndex = index; diff --git a/src/types.ts b/src/types.ts index 92ae08b..d12453e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,11 +1,19 @@ /** A pin's numbered position; all pins are open at position 4. */ export type Position = 1 | 2 | 3 | 4 | 5 | 6 | 7; + /** Direct influence from links[source][target]; the diagonal must be zero. */ export type Link = -1 | 0 | 1; + /** Signed numeric pin displacement in one action; zero is not an action. */ export type Delta = -6 | -5 | -4 | -3 | -2 | -1 | 1 | 2 | 3 | 4 | 5 | 6; + export type State = readonly [Position, Position, ...Position[]]; export type Command = readonly [index: number, delta: Delta]; + +/** Dynamic arrays are runtime checked; fixed tuples need at least two plates. */ +export type StateConstraint = + number extends S['length'] ? unknown : S extends State ? unknown : never; + /** A square matrix when S has a fixed tuple length; runtime-checked otherwise. */ export type Links = { readonly [Row in keyof S]: { readonly [Column in keyof S]: Link }; diff --git a/tests/unit/core-limits.test.ts b/tests/unit/core-limits.test.ts index cf68c8c..f949a00 100644 --- a/tests/unit/core-limits.test.ts +++ b/tests/unit/core-limits.test.ts @@ -2,7 +2,7 @@ import { expect, it } from 'vitest'; import { SearchLimitError } from '../../src/index.ts'; import { LockModel } from '../../src/lock.ts'; import { PreparedSearch, movementLimits } from '../../src/lock.ts'; -import { SearchBudget } from '../../src/astar.ts'; +import { SearchBudget } from '../../src/config.ts'; import { createSolverConfig } from '../../src/index.ts'; import { BfsSearch } from '../../src/bfs.ts'; import { MatrixSearch } from '../../src/astar.ts'; diff --git a/tests/unit/core-oracle.test.ts b/tests/unit/core-oracle.test.ts index c8611b4..4fb7aa1 100644 --- a/tests/unit/core-oracle.test.ts +++ b/tests/unit/core-oracle.test.ts @@ -3,7 +3,7 @@ import { solveLock } from '../../src/index.ts'; import type { Link, Position } from '../../src/index.ts'; import { LockModel } from '../../src/lock.ts'; import { PreparedSearch } from '../../src/lock.ts'; -import { SearchBudget } from '../../src/astar.ts'; +import { SearchBudget } from '../../src/config.ts'; import { createSolverConfig } from '../../src/index.ts'; import { BfsSearch } from '../../src/bfs.ts'; import { referenceActions, replay } from './oracle.ts'; diff --git a/tests/unit/release-network.test.ts b/tests/unit/release-network.test.ts index ea934c2..e923d66 100644 --- a/tests/unit/release-network.test.ts +++ b/tests/unit/release-network.test.ts @@ -73,7 +73,7 @@ describe('npm and CDN delivery boundaries', () => { }); }); -function mockGithub(directory: string, options: { draft: boolean; missing?: string; tagSha?: string; newer?: boolean; onSecondPage?: boolean }): void { +function mockGithub(directory: string, options: { draft: boolean; missing?: string; tagSha?: string; newer?: boolean; onSecondPage?: boolean; corruptAsset?: string }): void { const tag = `v${manifest.version}`; const filenames = [...releaseAssets, 'release-manifest.json']; const release = { tag_name: tag, draft: options.draft, prerelease: false, assets: filenames.filter(name => name !== options.missing).map(name => ({ name })) }; @@ -82,7 +82,7 @@ function mockGithub(directory: string, options: { draft: boolean; missing?: stri if (args[0] === 'api') { const endpoint = args[1] ?? ''; if (endpoint.includes('matching-refs')) return JSON.stringify([{ ref: `refs/tags/${tag}` }]); - if (endpoint.includes('/git/ref/tags/')) return JSON.stringify({ object: { type: 'commit', sha: options.tagSha ?? manifest.sourceSha } }); + if (endpoint.includes('/commits/refs/tags/')) return JSON.stringify({ sha: options.tagSha ?? manifest.sourceSha }); if (endpoint.includes('releases?')) { const firstPage = options.onSecondPage ? Array.from({ length: 100 }, (_, index) => ({ tag_name: `v2.0.0-canary.${index}`, draft: false, prerelease: true })) : [release]; const pages = [firstPage, ...(options.onSecondPage ? [[release]] : []), ...(options.newer ? [[{ tag_name: 'v1.1.0', draft: false, prerelease: false }]] : [])]; @@ -91,9 +91,15 @@ function mockGithub(directory: string, options: { draft: boolean; missing?: stri if (endpoint.includes('/releases/tags/')) return JSON.stringify(release); } if (args[0] === 'release' && args[1] === 'download') { - const filename = args[args.indexOf('--pattern') + 1]; const destination = args[args.indexOf('--dir') + 1]; - if (!filename || !destination) throw new Error('Missing download arguments'); - writeFileSync(join(destination, filename), readFileSync(join(directory, filename))); + const destination = args[args.indexOf('--dir') + 1]; + if (!destination) throw new Error('Missing download directory'); + for (const [index, argument] of args.entries()) { + if (argument !== '--pattern') continue; + const filename = args[index + 1]; + if (!filename) throw new Error('Missing asset pattern'); + const bytes = options.corruptAsset === filename ? Buffer.from('changed asset') : readFileSync(join(directory, filename)); + writeFileSync(join(destination, filename), bytes); + } return ''; } if (args[0] === 'release' && ['upload', 'edit'].includes(args[1] ?? '')) return ''; @@ -103,6 +109,17 @@ function mockGithub(directory: string, options: { draft: boolean; missing?: stri } describe('GitHub partial-release recovery', () => { + it('rejects changed downloaded assets before publishing the draft', async () => { + const directory = await releaseDirectory(); + try { + mockGithub(directory, { draft: true, corruptAsset: 'benchmark-evidence.json' }); + await expect(completeGithubRelease(directory, manifest, 'owner/repo')).rejects.toThrow(/identity/); + expect(command.mock.calls.some(([, args]) => args[1] === 'edit')).toBe(false); + } finally { + await rm(directory, { recursive: true, force: true }); + } + }); + it('rejects an existing tag at a different commit before uploading or finalizing', async () => { const directory = await releaseDirectory(); try { diff --git a/wiki/development.md b/wiki/development.md index f0d2491..87d5cdf 100644 --- a/wiki/development.md +++ b/wiki/development.md @@ -28,6 +28,12 @@ CLI invokes the exported solver/factory. The model, matrix analysis, searches an priority queue each own one responsibility. Config/default validation lives in `config.ts`. Classes encapsulate useful state; arithmetic uses plain functions. +Use domain names, named mechanical bounds and explicit units for packed state or +byte arithmetic. Keep one operation per statement and short loop bodies. Matrix +iteration remains explicit; search and release orchestration should read as ordered +steps. Oxlint limits block nesting to three levels in source and delivery scripts. +Review these properties independently of test success. + Tests live in `tests/unit`, `tests/e2e` and `tests/benchmarks`. TS consumer fixtures are compiled into ignored output when JavaScript is needed. Vite bundles the production files; `.github/scripts` coordinates existing build/release tools. diff --git a/wiki/releasing.md b/wiki/releasing.md index 3c2eaf8..d52a8a2 100644 --- a/wiki/releasing.md +++ b/wiki/releasing.md @@ -25,6 +25,22 @@ All referenced Actions are from their tool/service authors and pinned to verifie full commit SHAs. pnpm owns dependency installation, Vite owns bundling, and Vitest/Playwright own the checks. Workflow YAML only connects working local steps. +## One workflow, three jobs + +`release.yml` runs PR verification, stable publication on main and optional manual +canaries. Its check name is `CI`. A main push runs the verification sequence once. + +| Job | Responsibility | Publication permission | +| --- | --- | --- | +| Verification | Local checks, one archive across Node 22/24/26, browser consumers and the paired benchmark | None; repository read only | +| npm delivery | Prepare the verified files using trusted main tooling, publish or resume the identical archive, verify npm/CDN delivery | npm OIDC | +| GitHub release | Verify the delivered assets, resume or create the release and finalize it | Repository contents write | + +PRs finish after verification. Publication jobs require main workflow context and +the readiness setting. Candidate code runs in the readonly job; publication tools +come from a separate trusted main checkout. The same archive moves between jobs +without rebuilding. Failed delivery retains its evidence for recovery. + ## Local acceptance precedes workflow integration Run the full local test command, coverage, clean installed package verification @@ -73,8 +89,8 @@ commit and prepares one immutable candidate: 2. Verify and retain one tarball containing the five runtime files, declarations, package metadata, README and license. No consumer installation script builds it. 3. Publish that exact tarball to public npm with the agreed SemVer and `latest` tag. -4. Download the exact published version. Check registry integrity, tarball/file - identity, installation and module behavior without consumer credentials. +4. Download the exact published version without consumer credentials. Verify + its tarball is byte-identical to the archive already installed and tested. 5. Verify all four core file URLs on jsDelivr for status, JavaScript MIME, CORS and SHA256. Load them in real Chromium, Firefox and WebKit from another origin and solve all 45 fixtures. The fifth file is a Node tool, not a browser script.