Когда одинаковая строка скрывает разные решения
Техническая проблема не в том, что три языка «разные». Она в том, что граница часто получает один неименованный payload и предлагает каждому участнику восстановить type, error и time по привычке. Цена — ложный успех: adapter может вернуть похожий объект, но потерять distinction между отсутствующим значением, ошибкой и неподтверждённой длительностью.
Нельзя лечить это каталогом особенностей PHP, JavaScript и D. Такой каталог быстро становится неточным и всё равно не даёт rule на границе. Вместо него mechanism article строит три явные оси для fixed literal: tagged type, named error envelope и ordered logical time. Их проверяет JS-функция, но она не моделирует и не запускает иной runtime.
Три оси, которые нельзя склеить
Тип отвечает на вопрос «какой вид значения разрешён». Ошибка отвечает «в каком режиме завершилась операция и что делать с ним в рамках record». Время отвечает «какой интервал определён в выбранной шкале». Если поле отсутствует, нельзя заменить его соседней осью. Например, error code не даёт timestamp, а числовая величина не подтверждает value tag.
ECMA-262 16th edition отдельно описывает language types и completion records. Это полезная граница терминов, но не лицензия приписывать JavaScript общую семантику другим языкам. PHP 8.4.0 и D 2.111.0 в списке источников закреплены как отдельные исторические линии. В synthetic matrix они остаются только подписями model records.
| Ось | Допустимый fixed signal | Недопустимая подмена |
|---|---|---|
| type | value.tag = order-ready | coerced number вместо тега |
| error | named-envelope + code + retry | throw в одном record и envelope в другом |
| time | fixed-logical-ticks: 100..108 | wall-clock без закрытия |
| adapter | version и shape совпадают | local default или implicit conversion |
Почему tag важнее похожего shape
JSON-подобный shape создаёт опасную иллюзию одинаковости. У двух объектов могут совпадать поля amountMinor и currency, но один означает подготовленное решение, а другой — неполное. Named tag делает это различие проверяемым. Функция не выводит tag по наличию amount и не пытается догадаться о версии контракта.
Такой выбор сознательно скучный. Он заставляет автора назвать новый вариант и добавить fixture. Зато рецензент не ищет смысл в convention, которой нет в данных. Coercion тоже запрещён: mapping: coerced-number останавливает review. Это не оценка конкретного языка и не утверждение, что coercion всегда плох; это правило одного узкого boundary contract.
Ошибка — не транспорт чужого исключения
Error envelope содержит три fixed факта: semantics, code и retry. В accepted literal semantics равны named-envelope, code равен null, retry — not-requested. Такой набор может выразить отсутствие ошибки без передачи stack trace или object identity. Он не выбирает стандартный тип ошибки для PHP, JavaScript или D.
Смешанный случай важнее учебного успеха. Если php и d records пишут envelope, а javascript record отмечен thrown-value, boundary не имеет общего смысла, даже если все три человекочитаемых сообщения похожи. Возврат stop-incomparable-adapter не исправляет и не ловит исключение. Он отказывается склеить разные семантики в один вывод.
Время — это не число без основания
Временная часть intentionally excludes clock. Fixed logical ticks — целые значения, заданные прямо в literal. Они подходят только для вопроса «есть ли упорядоченный интервал внутри этого record». В accepted path 100..108 даёт восемь ticks. Это не миллисекунды, не latency и не performance observation.
Если basis поменялся на wall-clock либо closed отсутствует, функция возвращает stop-undetermined-time-boundary. Она не подставляет текущее время, не вычитает дату и не ищет внешнюю трассу. Так output не делает вид, будто время известно. В реальном проекте измерение потребует собственной границы, источника и policy; здесь оно только запрещено.
Исполняемая matrix
import { createFixedSemanticMatrix, explainFixedSemanticBoundary } from './upgrade-2026-06.mjs';
const matrix = createFixedSemanticMatrix('named-contract-v1');
const explanation = explainFixedSemanticBoundary(matrix);
console.log({
status: explanation.status,
typeRule: explanation.typeRule,
timeRule: explanation.timeRule,
effect: explanation.effect,
});
// synthetic-semantic-boundary-explained, one-named-value-tag-with-explicit-fieldsЭтот export добавляет к accepted review четыре named rules, но не меняет результат на сертификат. synthetic-semantic-boundary-explained означает, что внутри fixed matrix type, error, time и mapping были названы. Он не сообщает, как скомпилируется D-код, что бросит PHP или какой host определит JavaScript completion.
Последовательность разбора
- Проверить schemaVersion и operation; без них нельзя сравнивать adapters.
- Проверить tag до числовых полей и не делать inference по shape.
- Проверить error envelope целиком, включая null code и retry.
- Проверить единую basis и порядок opened/closed.
- Сравнить каждый adapter с contract, а не adapters друг с другом.
- Вернуть explanation только после exact mapping; иначе сохранить stop reason.
Что даёт stop каждому владельцу
Stop — это рабочий контракт, а не ругательство. contract-schema-or-named-value-missing назначает следующий шаг: назвать схему, operation и tag. adapter-does-not-preserve-contract-shape просит выровнять version, value tag и error envelope. adapter-time-basis-does-not-match-contract не спорит о точности часов; он требует одно основание именно для данного вопроса.
Благодаря такой точности нельзя выдать один отказ за другой. Incomplete contract не равен mixed errors. Mixed errors не равны incomparability от coercion. Undetermined time не равен длинному интервалу. Эти distinctions поддерживают T-shaped работу: автор понимает контрактную грань, но не изображает специалиста по каждому runtime без запуска и доказательства.
Границы типов на языке поля, а не реализации
Type rule здесь не говорит «используйте строгую типизацию». Он говорит гораздо уже: consumer видит только named tag и заранее определённые fields. Поэтому неважно, как конкретная реализация хранит целое число или где проверяет nullable value. Пока значение не попало в synthetic record, статья ничего не утверждает. После попадания reader не имеет права реконструировать дополнительные детали из названия model или из знакомого ему поведения языка.
Это различие полезно в review. Фраза «у нас там число» не является доказательством совместимости, пока не названы tag, unit и пустой случай. Фраза «у нас объект ошибки» тоже не говорит, какой режим завершения переносится. Fixed matrix принуждает переводить обе фразы в fields. Если перевод невозможен, это не повод искусственно унифицировать модели; это повод оставить boundary без accepted result.
Ещё один ложный shortcut — сравнить serialised text. Одинаковая строка может быть создана из разных source types, а разная строка может корректно представлять один named tag. Matrix не оценивает serialization. Она проверяет record после того, как serialization уже вынесена за границу. Так работа остаётся T-shaped: автор глубоко знает правило контракта, но не делает вид, что охватил все codec и language bindings.
Error semantics как отдельный договор
Named envelope выбран не из-за удобства JSON. Он выбран потому, что error mode должен быть наблюдаемым полем, а не побочным эффектом чтения. В example code и retry остаются явными даже при null code. Благодаря этому проверка отличает «значение отсутствует в envelope» от «сам envelope не был задан». Эти ситуации нельзя объединять: первая допускается одним literal, вторая закрывает contract.
Смешанный error mode особенно опасен в документации. Автор может сказать, что adapter «умеет бросать или возвращать ошибку», а consumer начнёт проверять только удобный путь. Fixed reviewer поступает иначе: presence thrown-value в одном adapter record не трактуется и не оборачивается. Он делает результат несопоставимым. Это освобождает текст от недоказуемых утверждений о stack, class и catch semantics конкретной среды.
Время как порядок, а не наблюдение
Logical ticks помогают отделить порядок шага от наблюдаемой длительности. У интерактивной системы могут быть задержки, планировщик и внешний transport, но none of them присутствуют в literal. Когда boundary сохраняет 100 и 108, он утверждает только то, что author fixed two ordered integers. Такой минимализм не беднее реального наблюдения; он честнее к поставленному вопросу.
Нельзя заменить missing closed tick формулой или приблизить его текущим моментом. Тогда тест станет зависеть от clock, а статья начнёт говорить о runtime effect. Rule требует, чтобы time basis совпала также у adapters. Этот шаг ловит ситуацию, где верхний contract использует logical ticks, а один participant тайно переименовал их в wall-clock. Даже при равных числах это разные единицы вывода.
Практический способ читать fixture
- Сначала смотреть на expected status, а не на реализацию проверки.
- Для каждого fail case назвать единственное изменённое свойство literal.
- Проверить, что status не шире причины: time case не сообщает ничего об error.
- Проверить, что accepted case не содержит слов о связи processes или services.
- Добавлять новый case только для нового rule, а не для повторения happy path.
Fixture тем самым работает как маленькая карта допустимых утверждений. Она не обеспечивает полноту языка, но показывает, где text обязан остановиться. Это важнее для boundary documentation, чем набор эффектных примеров: документ должен пережить смену реализации, не обещая того, что проверка не могла наблюдать.
Предел модели
Matrix не содержит union types, nullable policy, memory layout, stack unwinding, scheduling, ABI, serialization format или schema registry. Она не использует сеть, filesystem, clock, telemetry, profile, trace и PII. Эти отсутствия намеренные. Добавлять их через «ещё одно поле details» опасно: получится контейнер, который снова потребует неявной интерпретации.
У модели нет права объявить error recoverable, type compatible или time measured вне своих literals. Если следующее решение требует это сказать, его нужно описать отдельным пакетом с собственными sources и границами. Надёжность этого текста как раз в том, что он не берёт чужую доказательную нагрузку.
Следующий шаг
Добавьте fixture, где value tag сохранён, но один adapter записывает другую schemaVersion. Она должна вернуть точный stop, не accepted result. Затем проверьте, что опубликованный пример по-прежнему не имеет общих длинных фрагментов с practice и field article. Это поддерживает три самостоятельные идеи: boundary, semantics и evidence review.
Проверяемые источники
- ECMA-262, 16th edition: ECMAScript 2025 Language Specification — версия: 16th edition, June 2025, edition-specific publication. Показывает, что edition отдельно называет language types и completion records. Граница: Из этого не следует модель ошибок PHP или D.
- PHP source: annotated tag php-8.4.0 — версия: annotated tag php-8.4.0, tag object 501fc4ce253940b58a8744b1083951327e07f00e, 19 November 2024, immutable commit pin. Даёт проверяемую версию PHP source tree для исторической привязки. Граница: Статья не приписывает этому дереву формат межъязыкового адаптера.
- D compiler: annotated tag v2.111.0 — версия: annotated tag v2.111.0, tag object 6ae81e26cedc8f792d0074186e38974542f1a3cc, 31 March 2025; release 1 April 2025, immutable commit pin. Даёт проверяемую версию D compiler source tree для исторической привязки. Граница: Статья не делает утверждения о запущенном компиляторе или runtime.