<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<title>WEOUR Build — журнал разработки</title>
		<link>https://weour.com/ru/build/</link>
		<description>Публичный журнал разработки WEOUR OS: мультимагазины, складские системы, сопоставление SKU и AI-агенты — всё в продакшене.</description>
		<language>ru</language>
		<atom:link href="https://weour.com/ru/build/feed/" rel="self" type="application/rss+xml" />
		<atom:link href="https://weour.com/build/feed/" rel="alternate" hreflang="en" type="application/rss+xml" />
		<atom:link href="https://weour.com/ru/build/feed/" rel="alternate" hreflang="ru" type="application/rss+xml" />
		<atom:link href="https://weour.com/zh/build/feed/" rel="alternate" hreflang="zh-CN" type="application/rss+xml" />
		<lastBuildDate>Wed, 14 Oct 2026 00:00:00 +0000</lastBuildDate>
		<item>
			<title>Почему одна этикетка 80×80 мм печаталась на двух страницах?</title>
			<link>https://weour.com/ru/build/why-an-80x80mm-label-printed-two-pages/</link>
			<guid isPermaLink="true">https://weour.com/ru/build/why-an-80x80mm-label-printed-two-pages/</guid>
			<pubDate>Wed, 14 Oct 2026 00:00:00 +0000</pubDate>
			<description><![CDATA[Обычная кнопка скачивания выявила одну связанную задачу на стыке бизнес-идентификатора, имён файлов, браузерной пагинации и физических размеров; этикетку не уменьшали — изолировали документ для печати.]]></description>
			<content:encoded><![CDATA[<p>Всё началось с небольшого запроса: кнопка «Скачать PNG» в редакторе этикеток должна была открывать настоящий диалог Save As. После нажатия пользователь должен был увидеть понятное имя, выбрать место сохранения и получить файл, который можно опознать, не открывая.</p>
<p>Первая же версия имени выявила более глубокую проблему. Она использовала внутренний SKU вроде <code class="weour-build-inline-code">SKU-001</code>, хотя на самой этикетке люди видели и использовали <code class="weour-build-inline-code">Batch No: 30267-0914</code>. Скачивание работало, но семантика данных была неверной: система выбрала технически существующий, но бесполезный в работе идентификатор.</p>
<dl class="weour-build-facts"><div class="weour-build-fact"><dt>Адрес в Production</dt><dd>/tools/label/</dd></div><div class="weour-build-fact"><dt>Бизнес-идентификатор в имени файла</dt><dd>Видимый Batch No, если он задан</dd></div><div class="weour-build-fact"><dt>Пример</dt><dd>30267-0914-BQ.png · 30267-0914-BQ.pdf</dd></div><div class="weour-build-fact"><dt>Физический размер</dt><dd>Без масштабирования; 80×80 мм остаются 80×80 мм</dd></div><div class="weour-build-fact"><dt>Проверка пагинации</dt><dd>Шесть размеров дали по одной странице в Chromium</dd></div></dl>
<h2 id="section">Имя файла — часть модели данных</h2>
<p>Правильным решением была не ещё одна функция именования специально для скачивания. Имя файла и видимое поле <code class="weour-build-inline-code">Batch No</code> теперь берут значение из одного canonical-источника. Если на этикетке стоит <code class="weour-build-inline-code">30267-0914</code>, для PNG предлагается <code class="weour-build-inline-code">30267-0914-BQ.png</code>, а для PDF — <code class="weour-build-inline-code">30267-0914-BQ.pdf</code>. Если эти значения расходятся, как минимум одно из них уже не описывает партию, которую видит пользователь.</p>
<p>Для PDF действует тот же принцип, но интерфейс браузера устроен иначе. В macOS при переходе из браузерной печати к Print → Save as PDF предлагаемое имя PDF берётся из заголовка печатаемого документа. Поэтому перед печатью <code class="weour-build-inline-code">document.title</code> ненадолго заменяется на имя из Batch No, а после завершения, отмены или возврата управления окну обычный заголовок надёжно восстанавливается. Это не смена SEO-заголовка, а ограниченное состояние с явными границами одного сеанса печати.</p>
<h2 id="section">Одна страница превратилась в одну страницу и пустой лист</h2>
<p>Когда имена стали согласованными, проявился более интересный дефект: этикетка 80×80 мм давала в предпросмотре две страницы, причём вторая была пустой.</p>
<p>Кажется очевидным, что лист 80×80 мм должен помещаться на странице 80×80 мм. Но движок браузера не сравнивает два целых значения в миллиметрах. CSS-миллиметры превращаются в CSS-пиксели, а печатный конвейер ещё раз переводит их в единицы устройства и пагинации. На границе страницы субпиксельное округление, стандартный строчный блок или небольшой вклад body могут вытолкнуть математически «точный» край в следующий pagination fragment.</p>
<p>Исходный print sheet имел размеры страницы, но всё ещё участвовал в обычной пагинации документа. Скрыть только <code class="weour-build-inline-code">#root</code> было недостаточно: такой подход предполагал, что ни один другой дочерний элемент body не повлияет на печатную раскладку. Для точной границы страницы эта гарантия слишком слабая.</p>
<h2 id="section">Исправляем пагинацию, а не размер этикетки</h2>
<p>Правильное исправление изолирует print sheet от обычного потока пагинации. Печатные стили строго убирают все непечатные дочерние элементы body, обнуляют margin, padding и вклад строчного блока у <code class="weour-build-inline-code">html</code> и <code class="weour-build-inline-code">body</code>, а единственный печатаемый лист абсолютно позиционируют в начале страницы. Точное динамическое правило по текущей этикетке сохраняется:</p>
<pre class="weour-build-code"><code class="language-css">@page { size: &lt;width-mm&gt; &lt;height-mm&gt;; margin: 0; }</code></pre>
<p>Мы намеренно не использовали масштаб 99%, не вычитали по 1 мм из каждой стороны и не переносили этикетку на A4 с опцией «вписать в страницу». Такие приёмы могли бы убрать пустой лист, но одновременно нарушили бы главный контрак вывода. Этикетка 80×80 мм должна физически оставаться 80×80 мм.</p>
<p>Редактор и предпросмотр экспорта можно увеличить до 150%, чтобы с ними было удобнее работать, но это только способ показа. Canonical geometry и число пикселей не меняются: 100×100 мм по-прежнему экспортируются как 1000×1000 px, а 70×20 мм — как 700×200 px. Больший вид на экране не означает больший размер на бумаге.</p>
<h2 id="section">Как отличить исправление пагинации от удачного исключения</h2>
<p>Проверять только 80×80 мм было нельзя: специальный случай для одного размера легко создаёт убедительную, но хрупкую иллюзию. Проверка пагинации Chromium охватила 70×20, 70×40, 80×60, 80×80 и 100×100 мм, а также нестандартный размер 93×57 мм. Все шесть вариантов дали ровно одну страницу.</p>
<p>Автоматизированная среда не могла напрямую прочитать счётчик страниц в нативном предпросмотре печати macOS. Границу доказательств нужно формулировать точно: автоматика доказала, что печатные документы Chromium во всех шести размерах одностраничны; она не имитировала чтение недоступного значения из нативного UI.</p>
<p>Последний шаг произошёл за пределами браузера. После проверки print hotfix в Production содержимое проверенной ветки было сверено и возвращено в GitHub <code class="weour-build-inline-code">main</code>, так что source tree и Production tree совпали. Это закрытие не позволит уже исправленной ошибке вернуться при следующем развёртывании из неполного исходного состояния.</p>
<blockquote class="weour-build-quote">«Скачивается» ещё не значит «готово», как и «печатается». Готово — это когда источник данных, имя файла, физические размеры, пагинация и исходники Production согласованы между собой.</blockquote>]]></content:encoded>
			<category>Инженерия</category>
			<category>Моделирование данных</category>
			<category>Build in Public</category>
		</item>
		<item>
			<title>Мы перестали строить ещё одну ERP. Вот чем становится WEOUR OS</title>
			<link>https://weour.com/ru/build/we-stopped-building-another-erp/</link>
			<guid isPermaLink="true">https://weour.com/ru/build/we-stopped-building-another-erp/</guid>
			<pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate>
			<description><![CDATA[WEOUR OS больше не пытается стать полноценной ERP для трансграничной торговли. Система становится слоем над ERP и маркетплейсами, который превращает их данные в достоверные, объяснимые и применимые управленческие решения.]]></description>
			<content:encoded><![CDATA[<p>В сентябре мы писали здесь, что не стали покупать ещё одну ERP и вместо этого строим операционную систему для торговли. Эта заметка — о том, что произошло дальше: 2026-10-03 мы решили больше не разрабатывать те части системы, с которыми ERP и так хорошо справляется.</p>
<p>Это изменение архитектуры и продуктовой стратегии, а не отказ от WEOUR OS.</p>
<dl class="weour-build-facts"><div class="weour-build-fact"><dt>Дата решения</dt><dd>2026-10-03</dd></div><div class="weour-build-fact"><dt>Что прекращаем</dt><dd>Повторно разрабатывать заказы, запасы, закупки, складской учёт и финансы — базовые функции, значительная часть которых уже покрыта зрелыми ERP-системами</dd></div><div class="weour-build-fact"><dt>Системы учёта (system of record)</dt><dd>ERP и маркетплейсы — там, где данные изначально принадлежат им</dd></div><div class="weour-build-fact"><dt>Чем становится WEOUR OS</dt><dd>Мультиплатформенные операционные данные · достоверная аналитика · поддержка решений · control tower · AI-агенты</dd></div><div class="weour-build-fact"><dt>Что это за заметка</dt><dd>Запись стратегического решения, а не анонс релиза</dd></div></dl>
<h2 id="section">С чего мы начинали</h2>
<p>WEOUR OS задумывалась широко: одна система, которая понимает магазины, листинги, товары, склады и остатки так, как на самом деле устроен наш бизнес на нескольких маркетплейсах. Предыдущие заметки в этом журнале описывают эту работу: магазины с общим каталогом, но раздельными запасами, реальные складские ячейки, учётный Ledger и правило, по которому неизвестный остаток никогда не превращается в ноль.</p>
<p>Эта работа не прошла впустую. Она показала, где на самом деле находятся сложные задачи. И нагляднее любого плана показала другое: значительная часть полноценной Commerce OS состоит из задач, которые уже хорошо изучены и решены зрелыми системами.</p>
<h2 id="section">Что изменилось</h2>
<p>Когда мы перешли от описания к разработке, закономерность стала очевидной. Заказы, движение запасов, закупки, склад и финансы — большие, требующие аккуратности и давно изученные области. Зрелые ERP-системы, в том числе Lingxing, уже покрывают значительную часть базовых возможностей в этих областях и накопили большой опыт обработки пограничных случаев из реальной практики.</p>
<p>Каждая неделя, потраченная на повторную реализацию этих функций, уходила на то, чтобы догнать уже существующий продукт. В лучшем случае мы получили бы копию того, чем и так можем пользоваться. В худшем — второй источник истины для одного и того же заказа или одного и того же остатка. А именно такую неоднозначность WEOUR OS и должна устранять.</p>
<p>Поэтому мы остановились. WEOUR OS не будет заново разрабатывать типовые функции ERP, если нет задокументированной причины, по которой существующая система не закрывает конкретную потребность.</p>
<h2 id="section">Почему это правильный выбор</h2>
<p>Время инженеров — самый дефицитный ресурс. Важно не то, что мы в принципе можем построить, а то, где разработка создаёт ценность, которую больше никто не даст.</p>
<p>ERP отлично фиксирует то, что произошло в её собственных границах. Маркетплейс отлично отчитывается о том, что произошло на его площадке. Но ни та, ни другая система не рассчитаны на вопросы, из которых состоит ежедневное управление мультиплатформенным бизнесом: каким цифрам можно доверять прямо сейчас, где данные площадок расходятся, какое отклонение требует внимания в первую очередь и что с этим делать.</p>
<p>Эти вопросы лежат между системами. Именно здесь WEOUR OS может быть по-настоящему другой.</p>
<h2 id="weour-os">Чем становится WEOUR OS</h2>
<p>Меняется сама форма системы: вместо ещё одной «всё-в-одном» платформы для торговли — слой поверх систем, на которые мы уже опираемся:</p>
<pre class="weour-build-code"><code>ERP + маркетплейсы  →  слой аналитики и управления WEOUR OS  →  решения людей и агентов</code></pre>
<p>На практике это задаёт пять направлений дальнейшей работы. Именно направлений, а не пяти уже готовых возможностей:</p>
<ul class="weour-build-list"><li><strong>Мультиплатформенные операционные данные.</strong> Данные из ERP и с каждого маркетплейса сводятся в единую нормализованную картину, при этом сохраняются исходная запись и её происхождение.</li><li><strong>Достоверная аналитика.</strong> У каждого показателя есть свежесть и полнота. Неполные данные помечаются как неполные, а неизвестное никогда не выдаётся за ноль.</li><li><strong>Поддержка решений.</strong> Прибыльность, отклонения и прогнозы подаются как решения, которые нужно принять, а не как очередные отчёты.</li><li><strong>Control tower.</strong> Одно место, где видно состояние бизнеса на всех площадках и что требует внимания в первую очередь.</li><li><strong>AI-агенты.</strong> Агенты работают на проверенных данных и действуют в явно заданных границах прав доступа и аудита. Операции с высоким риском и ключевые управленческие решения требуют разрешения человека.</li></ul>
<p>ERP и маркетплейсы остаются системами учёта там, где это уместно. Подключение к ERP сейчас начинается в режиме только для чтения: WEOUR OS читает, объясняет и сверяет данные, но ничего не записывает обратно в систему, которой принадлежит запись. Это отправная точка интеграции, а не признак того, что она завершена.</p>
<h2 id="section">Чего это не означает</h2>
<p>Это не значит, что WEOUR OS начинается с нуля. Работа над идентификаторами, семантикой запасов, provenance данных и безопасностью релизов продолжается — именно она делает межплатформенные данные заслуживающими доверия.</p>
<p>Это также не значит, что новый слой уже готов. Значительная часть описанных здесь возможностей — поддержка решений, control tower, агенты — пока направление, а не функции, готовые к работе в production. Когда они появятся, мы зафиксируем их в этом журнале с тем же уровнем доказательности, что и всё остальное.</p>
<blockquote class="weour-build-quote">Ценность WEOUR OS не в том, чтобы иметь больше всех функций, а в том, чтобы превращать данные разных систем в решения, которым можно доверять, которые можно объяснить и на основе которых можно действовать.</blockquote>
<aside class="weour-build-callout"><p>Делая меньше того, что уже существует, мы получаем возможность делать больше того, чего ещё нет.</p></aside>
<p>WEOUR OS не нужно становиться заменой ERP. Ей нужно стать надёжным интеллектуальным слоем между ERP, платформами и людьми, которые управляют бизнесом.</p>]]></content:encoded>
			<category>WEOUR OS</category>
			<category>Архитектура</category>
			<category>Качество данных</category>
			<category>Build in Public</category>
		</item>
		<item>
			<title>Подключение заказов Yandex к единой операционной аналитике: запись сборки, основанная на доказательствах</title>
			<link>https://weour.com/ru/build/weour-os-program-milestone-yandex-ozon-yandex/</link>
			<guid isPermaLink="true">https://weour.com/ru/build/weour-os-program-milestone-yandex-ozon-yandex/</guid>
			<pubDate>Fri, 02 Oct 2026 00:00:00 +0000</pubDate>
			<description><![CDATA[Это событие подключило факты заказов двух магазинов Yandex к единому аналитическому контуру, общему с Ozon, и явно обозначило границу планировщика как «код развёрнут, но не включён». В этой заметке фиксируются только проверяемые доказательства и то, чего они не означают.]]></description>
			<content:encoded><![CDATA[<h2 id="section">Зачем</h2>
<p>До этого мультиплатформенное подключение и доказательства каталога, приём данных Yandex и единый аналитический контур продвигались разрозненно. Цель этого события — соединить их в одну цепочку: чтобы Ozon и Yandex использовали один и тот же факт заказов, а не поддерживали каждый свой контур.</p>
<p>Другой мотив — чётко обозначить границу. Код, связанный с планировщиком, уже вошёл в развёрнутый артефакт, но «код развёрнут» и «запущен в production по плану» — это две разные вещи. В этой записи последнее намеренно оставлено незаполненным, чтобы не описывать невключённую возможность как завершённое рабочее состояние.</p>
<h2 id="section">Что изменилось</h2>
<p>Изменения сосредоточены в нескольких блоках: подключение каналов, операционная аналитика и слой адаптации платформы Yandex — страницы каналов и аналитики в консоли, библиотека аналитики, библиотека каналов, слой адаптации платформы Yandex и серверная логика.</p>
<p>На стороне данных в каталог добавлено ограничение внешнего ключа, защищающее доказательства наблюдений и при этом не блокирующее сами строки задач — у этого ограничения есть соответствующее тестовое утверждение. На стороне каналов выполнен раунд исправлений RC, охватывающий приёмку, связанную с подключением магазинов, часовые пояса, общие ключи и список разрешённых PII.</p>
<p>На стороне документации зафиксированы два развёртывания: одно — развёртывание кода планировщика Yandex V1 и производственное наблюдение P5, другое — интеграция основного аналитического контура Yandex. Оба развёртывания записаны в обзорном документе.</p>
<p>Конкретные версии, коммиты, развёрнутые артефакты и дайджесты, количество тестов, состояние runtime и счётчики корпуса — см. facts выше.</p>
<dl class="weour-build-facts"><div class="weour-build-fact"><dt>Версия</dt><dd>20260926-yandex-sched-add2ba4 / 20260926-yaxan-1771e0c</dd></div><div class="weour-build-fact"><dt>Основной коммит релиза</dt><dd>025b05e54469a5e8ab636915322cbe478f73131e</dd></div><div class="weour-build-fact"><dt>Production provenance (коммит)</dt><dd>025b05e54469a5e8ab636915322cbe478f73131e</dd></div><div class="weour-build-fact"><dt>Развёрнутый артефакт</dt><dd>20260926-yandex-sched-add2ba4 (записано в docs/00-overview/CURRENT_STATE.md); 20260926-yaxan-1771e0c (записано в docs/00-overview/CURRENT_STATE.md)</dd></div><div class="weour-build-fact"><dt>Количество тестов</dt><dd>см. утверждение внешнего ключа каталога в коммите df4bb40802d4f0ee6af67d3bf7e09d74d39e7b5e</dd></div><div class="weour-build-fact"><dt>Состояние runtime</dt><dd>код планировщика развёрнут, не включён</dd></div><div class="weour-build-fact"><dt>Текущий авторский корпус</dt><dd>8 заметок / 24 variant (из них 1 ещё не опубликована)</dd></div></dl>
<h2 id="section">Как мы это проверили</h2>
<p>Проверка велась на трёх уровнях. Первый уровень — сам коммит: основной коммит релиза и коммит production provenance указывают на один и тот же SHA, что говорит о соответствии записи и развёрнутого артефакта.</p>
<p>Второй уровень — тесты: изменение внешнего ключа каталога сопровождается утверждением, проверяющим, что оно защищает доказательства наблюдений и при этом не запирает строки задач.</p>
<p>Третий уровень — документация: у каждого из двух развёртываний есть свой release id и дата записи, и они находятся в обзорном документе; архитектурный документ отдельно описывает дизайн планировщика заказов Yandex V1.</p>
<h2 id="section">Что это не значит</h2>
<p>Это не значит, что планировщик уже работает в production по плану. Код, вошедший в развёрнутый артефакт, и планировщик, который включён, поставлен в расписание и наблюдается как стабильно работающий, — это выводы разных уровней.</p>
<p>Это также не значит, что мультиплатформенное подключение завершено. Это событие подключило факты заказов Ozon и Yandex к единому контуру, но охват подключения, последующие платформы и операционные процессы не входят в эти доказательства.</p>
<p>Это равным образом не значит, что корпус полон. По состоянию на текущий момент в репозитории всё ещё есть неопубликованные заметки; счётчик корпуса — это состояние репозитория, зафиксированное в момент написания, и последующий рост репозитория не изменит смысл этого числа.</p>]]></content:encoded>
			<category>Build in Public</category>
		</item>
		<item>
			<title>Мы приостановили новые функции и сначала построили для WEOUR OS производственную страховочную сеть</title>
			<link>https://weour.com/ru/build/weour-os-building-the-safety-net/</link>
			<guid isPermaLink="true">https://weour.com/ru/build/weour-os-building-the-safety-net/</guid>
			<pubDate>Wed, 23 Sep 2026 00:00:00 +0000</pubDate>
			<description><![CDATA[В этом раунде WEOUR OS не получила новых функций — вместо этого были закрыты четыре вещи: хранение релизов, замкнутый контур резервного копирования, временной бюджет планировщика и единый canonical-конвейер публикации. Эта запись объясняет, что изменилось, какие были доказательства на тот момент и чего это не означает.]]></description>
			<content:encoded><![CDATA[<p>Это не выпуск функций. Здесь записано, почему WEOUR OS сначала остановила новые функции, чтобы построить слой производственной страховочной сети: резервное копирование, планирование, публикацию, откат.</p>
<dl class="weour-build-facts"><div class="weour-build-fact"><dt>Область этого раунда</dt><dd>Производственная инфраструктура (запись о строительстве этого раунда): резервное копирование · планирование · публикация · откат</dd></div><div class="weour-build-fact"><dt>Ритм резервного копирования</dt><dd>Раз в 6 часов (таймер launchd)</dd></div><div class="weour-build-fact"><dt>Контракт успешного резервного копирования</dt><dd>Четыре шлюза: stream / decrypt / structure / durable</dd></div><div class="weour-build-fact"><dt>Градация здоровья резервного копирования</dt><dd>HEALTHY ≤ 7 часов · DEGRADED ≤ 12 часов · STALE &gt; 12 часов (FAILED означает неудачу отдельной попытки, а не уровень)</dd></div><div class="weour-build-fact"><dt>Бюджет stagger планировщика</dt><dd>По умолчанию 30 секунд (нижняя граница 0 / верхняя граница 60), развязан с интервалом tick</dd></div><div class="weour-build-fact"><dt>Планировщик tick P50 (4 магазина)</dt><dd>119.5 s → 31.9 s</dd></div><div class="weour-build-fact"><dt>Планировщик tick P95 (4 магазина)</dt><dd>369.9 s → 55.3 s</dd></div><div class="weour-build-fact"><dt>Загрузка планировщика (duty cycle)</dt><dd>83.7% → 17.1%</dd></div><div class="weour-build-fact"><dt>Выборка приёмки планировщика</dt><dd>11 tick (4 магазина)</dd></div><div class="weour-build-fact"><dt>Первый Trial canonical-развёртывания</dt><dd>2026-09-23 неудача, конвейер выполнил откат</dd></div><div class="weour-build-fact"><dt>Производственная проверка этого раунда</dt><dd>release 20260923-rpg13-7f685ca (CANONICAL, validation / smoke PASS)</dd></div><div class="weour-build-fact"><dt>Тесты управления публикацией</dt><dd>123 → 157</dd></div><div class="weour-build-fact"><dt>Состояние хранения на момент наблюдения этого раунда</dt><dd>release 19 · кандидатов на удаление 1 · источник неизвестен 6 · защищено 5 (retention dry-run)</dd></div><div class="weour-build-fact"><dt>Коммит исправления планировщика</dt><dd>Anxin360/weour-os@e92b059</dd></div><div class="weour-build-fact"><dt>Коммит исправления readiness / iowait</dt><dd>Anxin360/weour-os@d4cbe01</dd></div><div class="weour-build-fact"><dt>Верхушка ветки trial Foundation V1.1</dt><dd>Anxin360/weour-os@b8cc817</dd></div><div class="weour-build-fact"><dt>Коммит управления V1.3</dt><dd>Anxin360/weour-os@7f685ca</dd></div></dl>
<h2 id="section">Почему сначала остановили новые функции</h2>
<p>WEOUR OS уже работает в production с реальными данными: реальные магазины, реальные циклы планировщика, реальные записи о публикациях. Чем больше функций, тем выше цена ошибки; но до этого несколько вещей не были по-настоящему замкнуты:</p>
<ul class="weour-build-list"><li>неизменяемые release только накапливались, диск сам не становился чище;</li><li>резервные копии были, но «действительно ли эта копия успешна» не проверялось автоматически, и не было сигнала здоровья, который можно было бы автоматически потреблять;</li><li>планировщик выглядел занятым, но занят он был в основном не бизнес-работой.</li></ul>
<p>Это не проблемы функций, а проблемы способа работы. Сначала их исправить — и каждая следующая функция обойдётся дешевле.</p>
<h2 id="section">Что изменилось</h2>
<h3 id="1-release-release">1. Хранение release: неизменяемым release нужна управляемая очистка</h3>
<p>Публикация — это неизменяемая цельная сборка: переключение делается символической ссылкой на новый каталог release, откат — переключением обратно на старый каталог. Такая схема делает откат дешёвым, но ценой постоянного роста диска — при аудите каталоги release занимали около 74% использованного диска этой машины (44.12 GB / 59.44 GB использовано), а на том же диске ещё нужно оставлять запас для сборки.</p>
<p>Поэтому очистка была написана как приоритет категорий, а не «удалить самое старое»:</p>
<pre class="weour-build-code"><code>CURRENT &gt; IMMEDIATE_ROLLBACK &gt; DIAGNOSTIC &gt; FAILED_TRIAL &gt; VALIDATED_ANCHOR
      &gt; MIGRATION_BOUNDARY &gt; MANUAL_PROTECT &gt; UNKNOWN_PROVENANCE &gt; ROUTINE</code></pre>
<p>Только <code class="weour-build-inline-code">ROUTINE</code> попадает в повседневную квотную очистку, остальные категории не удаляются попутно. Особенно важна одна из правил: release неудачного Trial не понижается до <code class="weour-build-inline-code">ROUTINE</code> только потому, что его commit позже попал в <code class="weour-build-inline-code">main</code>. Решение принимается только по доказательствам жизненного цикла, а не по имени каталога, временной метке или переписке.</p>
<h3 id="2">2. Резервное копирование: от «есть копия» к «проверяемой копии»</h3>
<p>Форма резервного копирования не переделывалась: хост резервного копирования сам забирает данные, production предоставляет только ограниченный поток принудительных команд, а закрытый ключ для расшифровки никогда не попадает на production-хост. Изменения произошли на стороне забора:</p>
<ul class="weour-build-list"><li><strong>Ритм и RPO</strong>: раз в 6 часов (таймер launchd), целевой RPO — 6 часов. Ритм объясним и не зависит от дрейфующего интервального таймера.</li><li><strong>Четыре шлюза</strong>: запуск считается успешным только после прохождения всех четырёх шлюзов — stream, decrypt, structure, durable. Код выхода <code class="weour-build-inline-code">pg_dump</code>, равный 0, сам по себе никогда не является успехом.</li><li><strong>Неудача есть неудача</strong>: при провале любого шлюза запуск получает <code class="weour-build-inline-code">FAILED</code>, <code class="weour-build-inline-code">last-success</code> не обновляется, очистка не выполняется.</li><li><strong>Градация здоровья</strong>: <code class="weour-build-inline-code">HEALTHY</code> (≤ 7 часов) / <code class="weour-build-inline-code">DEGRADED</code> (≤ 12 часов) / <code class="weour-build-inline-code">STALE</code> (&gt; 12 часов) выводится из «часов с момента последнего успеха» и может потребляться мониторингом напрямую как код выхода; <code class="weour-build-inline-code">FAILED</code> описывает отдельную попытку, а не уровень.</li><li><strong>Тесты с внедрением отказов</strong>: покрывают удалённый отказ, слишком маленький артефакт, ошибку расшифровки, недопустимую структуру, наложенный запуск, устаревшую блокировку, политику хранения и повреждённое состояние здоровья.</li></ul>
<p>Последний шаг — вернуть сигнал здоровья в production: после прохождения четырёх шлюзов хост резервного копирования через ограниченную учётную запись отправляет снимок в production; эта учётная запись может писать только один фиксированный файл, не может переключать release, не может перезапускать сервисы и тем более не может читать какие-либо ключи.</p>
<h3 id="3-tick-stagger">3. Планировщик: разделить семантику tick и stagger</h3>
<p>Диагностика сначала дала неудобный вывод: в интервале работы 4 магазинов 96.8% настенного времени tick уходило на детерминированный stagger-сон — из 2610 секунд 84 секунды были реальной работой. В расчёте на каждое action сам сон занимал 4150 секунд/час, почти 115% настенного времени.</p>
<p>Корневая причина — смешение семантики: окно stagger было привязано к интервалу tick (180 секунд), а фиксированные задержки нескольких action на одном последовательном runner складывались последовательно.</p>
<p>Исправление разделило две вещи: интервал tick используется только для отчётного cadence и больше не участвует в расчёте stagger; у stagger есть собственный бюджет, по умолчанию 30 секунд, допустимые значения 0 / 30 / 60, недопустимые значения откатываются к значению по умолчанию. Измерения после развёртывания:</p>
<ul class="weour-build-list"><li>tick P50 119.5 секунды → 31.9 секунды, P95 369.9 секунды → 55.3 секунды;</li><li>загрузка (duty cycle) 83.7% → 17.1%;</li><li>запланированный сон совпадает с фактическим, остальное время — объяснимая работа с API и DB;</li><li>timer, cadence, переключатели магазинов и бюджет параллелизма не менялись.</li></ul>
<h3 id="4-canonical">4. Единый canonical-конвейер публикации</h3>
<p>Публикация — больше не цепочка ручных команд, а упорядоченный воспроизводимый конвейер:</p>
<pre class="weour-build-code"><code>provenance → preflight → release lock → scheduler quiet window → source
  → build backend → monitored build → immutable release + manifest
  → candidate validation → rollback readiness → atomic switch → restart
  → readiness → functional smoke → state write → history → retention dry-run</code></pre>
<p>Несколько мест сделаны намеренно: сборка выполняется только в тихом окне планировщика; переключение атомарно; цель отката проверяется до и после переключения; код выхода <code class="weour-build-inline-code">systemctl restart</code> явно записывается как «юнит запущен», а не «приложение готово».</p>
<h3 id="5-trial">5. Первый Trial провалился — и был полностью сохранён</h3>
<p>Первый Trial canonical-развёртывания прошёл свой путь успеха, затем провалился на этапе smoke, и конвейер выполнил свой canonical-откат.</p>
<p>Причина не в приложении: <code class="weour-build-inline-code">systemctl restart</code> при возврате означает лишь, что юнит запущен, а не что порт уже привязан. Тогдашний smoke был немедленной HTTP-проверкой без какого-либо ожидания readiness, поэтому он увидел отказ соединения и ошибку шлюза.</p>
<p>Важен именно способ обработки: никто не обошёл конвейер, чтобы вручную исправить этот шаг. Конвейер остановился по своему контракту отказа и выполнил откат, этот release был сохранён для диагностики, а на этапе очистки не было выполнено ни одного удаления. Production продолжал нормально обслуживаться на release до переключения.</p>
<p>Последовавшее исправление сделало readiness ограниченным опросом, а мониторинг сборки — реально измеряющим iowait. При повторном прогоне прежний режим отказа был корректно поглощён новой логикой как временная неготовность, а не как неудача развёртывания.</p>
<h3 id="6">6. Управление: оставить доказательства и «кто может публиковать», и «что было опубликовано»</h3>
<p>Правила этого раунда добавлены для будущих себя:</p>
<ul class="weour-build-list"><li><strong>canonical deploy only</strong>: повседневная публикация идёт только через конвейер; исключения должны быть явно помечены.</li><li><strong><code class="weour-build-inline-code">deploymentMethod</code></strong>: каждая новая запись жизненного цикла несёт перечисление метода, исторические записи дописываются, а не переписываются; неопределимые исторические записи помечаются <code class="weour-build-inline-code">UNKNOWN_LEGACY</code> и никогда не угадываются как <code class="weour-build-inline-code">CANONICAL</code>.</li><li><strong>Классификация неудачного Trial</strong>: <code class="weour-build-inline-code">FAILED_TRIAL</code> определяется по доказательствам жизненного цикла и не занимает повседневную квоту очистки.</li><li><strong>Приоритет диагностических доказательств</strong>: перед удалением нужно записать обратно диагностическую запись уровня KB без ключей и проверить её; если записать не удаётся — удаление отклоняется.</li></ul>
<p>На точке наблюдения этого раунда (production release <code class="weour-build-inline-code">20260923-rpg13-7f685ca</code>) политика хранения представлена в dry-run: запись показывает 19 release, из них 1 кандидат на удаление, 6 с неизвестным источником, 5 защищённых. Очистка происходит только при полных доказательствах (цель подтверждена как неиспользуемая, диагностическая запись записана обратно и проверена).</p>
<h2 id="section">Как мы это проверили</h2>
<p>Способ проверки каждого звена по возможности опирается на перепроверяемые доказательства, а не на «выглядит нормально»:</p>
<ul class="weour-build-list"><li>резервное копирование проверено тестами с внедрением отказов и реальными прогонами четырёх шлюзов, сигнал здоровья потребляется мониторингом;</li><li>исправление планировщика сначала смоделировано в изолированной среде с измеренными offset, затем после развёртывания сравнено до и после на 11 реальных tick;</li><li>конвейер публикации проверен тем, что ему дали самостоятельно выполнить реальную публикацию: readiness пройден, functional smoke прошёл по шести маршрутам, состояние записано конвейером, и только затем выполнен dry-run retention;</li><li>правила управления покрыты тестами — от «неудачный Trial не понижается» до «публикатор отклоняет аномальную нагрузку».</li></ul>
<h2 id="section">Что это не значит</h2>
<p>Это не выпуск функций и не означает, что инфраструктура завершена.</p>
<p>В записи этого раунда ограничения резервного копирования прописаны явно: ключ расшифровки существует в единственном экземпляре и находится на той же машине, что и шифротекст; нет второй копии в другом месте; нет PITR, RPO — это и есть ритм в 6 часов; у резервного копирования медиа нет политики хранения; роли и глобальные объекты не входят в dump. Это известные компромиссы, а не проигнорированные детали.</p>
<p>Исправление планировщика решает только вопрос «куда уходит время», это не оптимизация параллелизма или производительности.</p>
<p>Управление также не покрывает всю историю: на точке наблюдения этого раунда всё ещё существуют старые release с неопределимым источником, они консервативно сохранены.</p>
<p>Эта запись относится к раунду строительства с 2026-09-22 по 2026-09-23. Все производственные состояния в тексте — факты <strong>на точке наблюдения этого раунда</strong>, а не текущее состояние в любой последующий момент; последующие производственные изменения вне рамок этой статьи.</p>
<p>И наконец, это не обещание будущих версий, сроков или возможностей. Здесь записано уже произошедшее обновление инфраструктуры и доказательства, которые его подкрепляют.</p>
<h2 id="section">Где доказательства</h2>
<p>Каждое фактическое утверждение в этой записи указывает на объект, который можно перепроверить: соответствие коммитов, идентификаторов развёрнутых release и количества тестов зарегистрировано в таблице фактов выше, а также в evidence-записи этой статьи. Основной текст лишь объясняет смысл и не повторяет перечень идентификаторов.</p>
<aside class="weour-build-callout"><p>Ценность страховочной сети не в том, что она построена, а в том, что когда она действительно остановит неудачу, никому не придётся на ходу решать, что делать.</p></aside>]]></content:encoded>
			<category>WEOUR OS</category>
			<category>Инженерия</category>
			<category>Архитектура</category>
			<category>Build in Public</category>
		</item>
		<item>
			<title>Мы превратили публикацию контента Build в проверяемый конвейер</title>
			<link>https://weour.com/ru/build/weour-core-1-3-5-release/</link>
			<guid isPermaLink="true">https://weour.com/ru/build/weour-core-1-3-5-release/</guid>
			<pubDate>Tue, 22 Sep 2026 00:00:00 +0000</pubDate>
			<description><![CDATA[Это событие свело путь публикации контента Build от ручных операций к конвейеру с предпросмотром, CI-шлюзами и контрактом контента и было проверено в production. Ниже зафиксировано, что изменилось, какие есть доказательства и что это не значит.]]></description>
			<content:encoded><![CDATA[<h2 id="section">Зачем</h2>
<p>До этого публикация контента Build зависела от ручного суждения: соответствие контента требованиям, полнота структуры и корректность чтения runtime после публикации не имели единых точек проверки. Как только формат контента расходился с ожиданиями runtime, проблема обычно обнаруживалась только после публикации.</p>
<p>Цель этого изменения — не добавить функциональность, а превратить «можно ли публиковать контент» в вопрос, на который можно ответить до публикации: контент сначала проходит проверку контракта, затем CI-шлюзы и только потом попадает в процесс публикации.</p>
<h2 id="section">Что изменилось</h2>
<p>Изменения сосредоточены в нескольких взаимосвязанных частях: workflow, каталог контента, документация, модуль контента runtime, скрипты и тесты.</p>
<p>На стороне контента введены определения schema и topics, которые делают явной связь между заметками, variant и topic; на стороне скриптов выполняется проверка контента по контракту; на стороне тестов покрываются оба пути проверки — PHP и Node; на стороне workflow предпросмотр и шлюзы связываются в цепочку, так что до публикации необходимо пройти проверки.</p>
<p>Сама публикация завершается одним развёртыванием в production; запись о развёртывании вносится в документацию репозитория и помечается соответствующей меткой проверки. Конкретные версии, коммиты, дайджесты артефактов, количество тестов и счётчики корпуса см. в блоке facts выше.</p>
<h2 id="section">Как мы это проверили</h2>
<p>Проверка состоит из двух уровней. Первый уровень — автоматические проверки: два набора проверок, PHP и Node, выполняются в CI, и настроено несколько шлюзов, продолжение возможно только после прохождения всех. Второй уровень — проверка в production: после развёртывания артефакта в production runtime сообщил о своём состоянии и объёме загруженного контента.</p>
<p>Нужно различать два среза корпуса на разные моменты времени:</p>
<ul class="weour-build-list"><li>Производственный корпус на момент развёртывания 1.3.5 | 5 заметок / 15 variant / 13 topics</li><li>Текущий авторский корпус | 6 заметок / 18 variant (из них 1 ещё не опубликована)</li></ul>
<p>Первое — снимок runtime на момент развёртывания, второе — текущее состояние контента в репозитории. Их несовпадение нормально: в репозитории могут быть ещё не опубликованные черновики.</p>
<dl class="weour-build-facts"><div class="weour-build-fact"><dt>Версия</dt><dd>1.3.5</dd></div><div class="weour-build-fact"><dt>Основной коммит релиза</dt><dd>57bf785a0bcd6e886397de776cbe7bd7156fd64c</dd></div><div class="weour-build-fact"><dt>Production provenance (коммит)</dt><dd>4b4ec32a3aac70a8222ca7255f0db38fb6683200</dd></div><div class="weour-build-fact"><dt>Развёрнутый артефакт</dt><dd>weour-core-1.3.5.zip</dd></div><div class="weour-build-fact"><dt>SHA-256 артефакта</dt><dd>f660c0d9b48cbe774a3ba10d380eedfbf9a4fee5cf1a49c97c024ee7c1ee28ab</dd></div><div class="weour-build-fact"><dt>SHA-256 манифеста плагина</dt><dd>952f570124787702fc8c71ce6ebd5410dca3099730b212d805189f3764d2a703</dd></div><div class="weour-build-fact"><dt>Среда развёртывания</dt><dd>production</dd></div><div class="weour-build-fact"><dt>Состояние развёртывания</dt><dd>deployed</dd></div><div class="weour-build-fact"><dt>Состояние runtime</dt><dd>ready</dd></div><div class="weour-build-fact"><dt>Версия runtime</dt><dd>1.3.5</dd></div><div class="weour-build-fact"><dt>Число модулей runtime</dt><dd>12</dd></div><div class="weour-build-fact"><dt>Количество тестов</dt><dd>PHP 1100 проверок / Node 97 проверок</dd></div><div class="weour-build-fact"><dt>Число шлюзов</dt><dd>5</dd></div><div class="weour-build-fact"><dt>Производственный корпус на момент развёртывания</dt><dd>5 заметок / 15 variant / 13 topics</dd></div><div class="weour-build-fact"><dt>Текущий авторский корпус</dt><dd>6 заметок / 18 variant (из них 1 ещё не опубликована)</dd></div></dl>
<h2 id="section">Что это не значит</h2>
<p>Это не значит, что публикация контента с этого момента не требует ручного суждения. Конвейер проверяет контракт и структуру, а не то, уместен ли сам контент и стоит ли его публиковать.</p>
<p>Это не значит, что весь контент в репозитории уже опубликован. Текущее количество контента в репозитории больше, чем количество в runtime на момент развёртывания; разница — это ещё не опубликованные черновики.</p>
<p>Это также не значит, что данный процесс охватывает все будущие формы контента. Эта проверка относится к текущему контракту контента и пути чтения runtime; формы за пределами контракта в объём этой проверки не входят.</p>
<p>Наконец, это не является обещанием относительно последующих версий, сроков или возможностей. Здесь зафиксированы одна уже состоявшаяся публикация и проверка, а также подтверждающие её доказательства.</p>]]></content:encoded>
			<category>Build in Public</category>
		</item>
		<item>
			<title>Запись реестра №1 — когда WEOUR OS начал знать, чем мы владеем</title>
			<link>https://weour.com/ru/build/ledger-1-when-weour-os-started-knowing-what-we-had/</link>
			<guid isPermaLink="true">https://weour.com/ru/build/ledger-1-when-weour-os-started-knowing-what-we-had/</guid>
			<pubDate>Mon, 21 Sep 2026 00:00:00 +0000</pubDate>
			<description><![CDATA[Первый реальный физический пересчёт попал в центральный реестр запасов, и что это изменило в разнице между «знать», «наблюдать» и «решать».]]></description>
			<content:encoded><![CDATA[<p>Месяцами честный ответ на вопрос «чем мы на самом деле владеем?» представлял собой набор чисел: откуда-то полученных, в несогласованных форматах, в несовпадающее время. Мы знали, что сообщают каналы. Реестра у нас не было.</p>
<p>Эта запись — момент, когда это изменилось. Запись реестра №1 — первая в WEOUR OS, где реальный физический пересчёт стал записанным запасом в форме, которую можно проверить позже.</p>
<h2 id="section">Что было до реестра</h2>
<p>К тому моменту два реальных склада уже были смоделированы, а их активные ячейки заменены на проверенные физические — те 1 306 ячеек из предыдущей записи. Маркетплейсы и каналы сообщали то, что видели. Ничто из этого не равно тому, что WEOUR знает.</p>
<p>Наблюдаемые запасы — это доказательство. Это то, что источник сообщил нам, в определённый момент, со своей точки зрения. Центральный реестр — другое: это место, где WEOUR решает, чем владеет, и он заслуживает этот статус только тогда, когда его можно проследить до физического факта.</p>
<h2 id="section">Что на самом деле меняет первый пересчёт</h2>
<p>Пересчёт — это не синхронизация. Это человек, который стоит перед ячейкой и подтверждает, что там находится. Когда результат попадает в реестр, впервые становятся возможны три вещи:</p>
<ul class="weour-build-list"><li>число существует потому, что кто-то его посчитал, и за ним стоят область, время и место;</li><li>разница между тем, что система показывала раньше, и тем, что найдено физически, становится записываемым движением, а не загадкой;</li><li>последующие наблюдения можно сравнивать с решением, а не друг с другом.</li></ul>
<dl class="weour-build-facts"><div class="weour-build-fact"><dt>Реальных складов в области пересчёта</dt><dd>2</dd></div><div class="weour-build-fact"><dt>Активных складских ячеек</dt><dd>1 306</dd></div><div class="weour-build-fact"><dt>Первая запись</dt><dd>Входящий остаток по результатам пересчёта</dd></div><div class="weour-build-fact"><dt>Движение в реестре</dt><dd><code class="weour-build-inline-code">OPENING_BALANCE +55</code></dd></div><div class="weour-build-fact"><dt>Автоматические записи запасов</dt><dd>Нет</dd></div><div class="weour-build-fact"><dt>Требование утверждения</dt><dd>Явное, по каждому классу движений</dd></div></dl>
<p><code class="weour-build-inline-code">OPENING_BALANCE +55</code> — это бухгалтерское движение, а не сигнал спроса. Оно фиксирует, что посчитанная реальность и реестр впервые согласны по числу в этой области. Мы публикуем его потому, что именно такие движения центральный складской контур обязан уметь объяснять месяцы спустя, — и потому что придумать более опрятное число означало бы уничтожить смысл всей затеи.</p>
<h2 id="section">Правила, которые это позволили</h2>
<p>Пересчёт можно было внести только потому, что в системе уже действовали четыре правила:</p>
<ol class="weour-build-list"><li><code class="weour-build-inline-code">NOT_INITIALIZED ≠ 0</code>. Область, которую никогда не считали, не пуста — она неизвестна и остаётся неизвестной, пока кто-то её не посчитает.</li><li><code class="weour-build-inline-code">NOT_REPORTED ≠ ZERO</code>. Канал, который не отчитался, не сообщил нам ничего, и неудачный отчёт никогда не превращается в дефицит.</li><li><code class="weour-build-inline-code">Observed Inventory ≠ Central Inventory</code>. Каналы продолжают сообщать то, что видят. Реестр фиксирует то, что WEOUR решил после сверки.</li><li><code class="weour-build-inline-code">Candidate ≠ Approved</code>. Предложенное движение — это предложение. Запасом оно становится только после явного утверждения и никогда — за счёт автоматического заполнения пробела.</li></ol>
<aside class="weour-build-callout"><p>Первая запись реестра ценна не своим числом. Она ценна тем, что всё вокруг неё — область, ячейка, время и утверждение — можно восстановить позже.</p></aside>
<h2 id="section">Чего это не означает</h2>
<p>Это не означает, что вопрос запасов «решён». Два посчитанных склада — это два посчитанных склада. Реестр хорош ровно настолько, насколько хорош следующий пересчёт, а следующая область — та, которая ещё не инициализирована.</p>
<p>Это также не означает, что числа окончательны. Пересчёт — утверждение о моменте. Система должна сохранять этот момент рядом с числом, чтобы позднейшая разница была разницей между двумя известными состояниями, а не спором о том, кто прав.</p>
<h2 id="section">Почему это первая запись реестра</h2>
<p>Мы публикуем серию о реестре, потому что это самая негламурная и самая несущая часть торговой операционной системы. Если система не может отдельно сказать, что она знает, что наблюдала и что решила, то все числа над ней — украшение.</p>
<p>Запись реестра №1 — первая, где эти три вещи перестали быть одной и той же вещью.</p>]]></content:encoded>
			<category>Запасы</category>
			<category>Центральные запасы</category>
			<category>Склад</category>
			<category>WEOUR OS</category>
			<category>Build in Public</category>
		</item>
		<item>
			<title>Неизвестные запасы — это не ноль</title>
			<link>https://weour.com/ru/build/unknown-inventory-is-not-zero/</link>
			<guid isPermaLink="true">https://weour.com/ru/build/unknown-inventory-is-not-zero/</guid>
			<pubDate>Sun, 20 Sep 2026 00:00:00 +0000</pubDate>
			<description><![CDATA[Принцип WEOUR OS: неинициализированные и неотчитавшиеся запасы не равны нулю, а наблюдаемые остатки никогда не являются центральными.]]></description>
			<content:encoded><![CDATA[<p>Это правило влияет на WEOUR OS сильнее любого другого, поэтому у него отдельная запись.</p>
<dl class="weour-build-facts"><div class="weour-build-fact"><dt>NOT_INITIALIZED</dt><dd>Системе никогда не сообщали, что здесь находится</dd></div><div class="weour-build-fact"><dt>NOT_REPORTED</dt><dd>Источник, который обычно отчитывается, не отчитался</dd></div><div class="weour-build-fact"><dt>ZERO</dt><dd>Мы знаем от конкретного источника и в конкретный момент, что количество равно нулю</dd></div><div class="weour-build-fact"><dt>Наблюдаемые запасы</dt><dd>То, что сообщил канал или склад, сохранённое как доказательство</dd></div><div class="weour-build-fact"><dt>Центральные запасы</dt><dd>То, что WEOUR решил считать истиной после сверки</dd></div></dl>
<p>Это пять разных вещей. Системы, которые сжимают их в одну колонку, не проще — они просто не могут объяснить, почему число именно такое.</p>
<h2 id="section">Почему «неизвестно» не должно становиться «нулём»</h2>
<p>Ноль — это утверждение. Он говорит, что измеримое количество равно нулю. Когда у системы нет информации, запись нуля делает неизвестный факт похожим на известный, и каждое последующее решение наследует эту ошибку:</p>
<ul class="weour-build-list"><li>Витрина помечает товар недоступным не потому, что товар закончился, а потому что сорвался отчёт.</li><li>Правило пополнения срабатывает на несуществующем дефиците.</li><li>Отчёт сверки показывает расхождение, которое на самом деле является отсутствующим источником, и команда тратит день на поиск проблемы, которой нет.</li></ul>
<p>Ничто из этого не выглядит как баг. Это выглядит как обычные данные.</p>
<h2 id="section">Как мы это представляем</h2>
<p>Незаданное состояние запаса хранится как явное состояние, а не как число. Состояния — часть модели:</p>
<ul class="weour-build-list"><li><code class="weour-build-inline-code">NOT_INITIALIZED</code> — запись существует, но ни один источник никогда не предоставлял значение.</li><li><code class="weour-build-inline-code">NOT_REPORTED</code> — источник должен отчитаться, но пока не сделал этого или отчёт не прошёл.</li><li>Числовое значение — фактическое утверждение, всегда вместе с источником и временем наблюдения.</li></ul>
<p>Слой отображения может быть дружелюбным. Слой хранения не имеет права угадывать.</p>
<h2 id="section">Наблюдаемые запасы — это не центральные запасы</h2>
<p>Вторая половина принципа: наблюдения никогда не становятся реестром «по случайности».</p>
<p>Каждый маркетплейс и каждый склад что-то сообщает, и эти сообщения отвечают на разные вопросы в разные моменты. Наблюдаемые запасы — это то, что нам сообщили. Центральные запасы — это то, что WEOUR решил после сверки наблюдений, физических пересчётов и известных пробелов.</p>
<aside class="weour-build-callout"><p>Система может показать вам наблюдение. Она не будет выдавать это наблюдение за решение.</p></aside>
<p>Именно явная граница между ними делает продажи через несколько магазинов честными: пять магазинов могут сообщать то, что видят, и ни один из них не переписывает молча то, чем владеет WEOUR.</p>
<h2 id="section">Сколько это стоит и почему мы платим</h2>
<p>Этот принцип неудобен. Он означает больше состояний, больше явной обработки и меньше мест, где значение по умолчанию может тихо закрыть дыру. Он означает, что дашборды должны говорить «неизвестно» вместо успокаивающего нуля.</p>
<p>Мы платим эту цену, потому что альтернатива — операционная система, которая уверенно ошибается. Commerce OS заслуживает доверия, когда знает, чего не знает, — и говорит об этом в базе данных, а не только в интерфейсе.</p>
<blockquote class="weour-build-quote"><code class="weour-build-inline-code">NOT_INITIALIZED ≠ 0</code>. <code class="weour-build-inline-code">NOT_REPORTED ≠ ZERO</code>. Наблюдаемые запасы — это не центральные запасы.</blockquote>]]></content:encoded>
			<category>Запасы</category>
			<category>Моделирование данных</category>
			<category>WEOUR OS</category>
			<category>Инженерия</category>
			<category>Build in Public</category>
		</item>
		<item>
			<title>1 306 реальных складских ячеек</title>
			<link>https://weour.com/ru/build/1306-real-warehouse-locations/</link>
			<guid isPermaLink="true">https://weour.com/ru/build/1306-real-warehouse-locations/</guid>
			<pubDate>Tue, 15 Sep 2026 00:00:00 +0000</pubDate>
			<description><![CDATA[Как placeholder-ячейки были заменены проверенными мастер-данными складов для операций в Сучжоу и Шэньчжэне.]]></description>
			<content:encoded><![CDATA[<p>Какое-то время наши складские данные выглядели здоровыми: у каждого товара была ячейка, каждая ячейка существовала в системе. Цифры были согласованными, и ничего не ломалось.</p>
<p>Ячейки были placeholder-значениями.</p>
<h2 id="section">Чем плохи числа, которые всего лишь согласованы</h2>
<p>Placeholder-ячейки легко генерировать и легко им доверять. Они подходят под схему, проходят валидацию и дают графики. Сбой проявляется только тогда, когда кому-то нужно физически взять товар, или когда два разных человека описывают одну и ту же полку двумя разными способами, а система настаивает, что это разные места.</p>
<p>В этом и ловушка: placeholder-данные не выглядят как отсутствующие. Они выглядят как данные.</p>
<h2 id="placeholder">Замена набора placeholder-значений на реальный</h2>
<p>Мы перестроили мастер-данные складов исходя из того, как площадки работают на самом деле, и загрузили их как авторитетный набор складских ячеек.</p>
<dl class="weour-build-facts"><div class="weour-build-fact"><dt>Проверенные складские ячейки</dt><dd>1 306</dd></div><div class="weour-build-fact"><dt>Смоделированные площадки</dt><dd>Сучжоу, Шэньчжэнь</dd></div><div class="weour-build-fact"><dt>Подход</dt><dd>Сначала физическое место, потом запись в системе</dd></div><div class="weour-build-fact"><dt>Placeholder-ячейки</dt><dd>Выведены из использования, а не «отремонтированы»</dd></div><div class="weour-build-fact"><dt>Неизвестный mapping</dt><dd>Остаётся неизвестным, пока реальная ячейка не подтверждена</dd></div></dl>
<p>Количество здесь не главное. Главное — что каждая из этих 1 306 ячеек соответствует месту, до которого человек может дойти, и что обе площадки смоделированы по одним правилам, а не по двум локальным привычкам.</p>
<h2 id="section">Что вскрылось при изменении</h2>
<p>Замена placeholder-данных реальностью обнажает всё, что тихо опиралось на фальшивые данные:</p>
<ul class="weour-build-list"><li>Товары, у которых была placeholder-ячейка и не было настоящего места, требовали решения, а не значения по умолчанию.</li><li>Легаси-коды, означавшие на разных площадках разное, нужно было разобрать, а не слить.</li><li>Любой процесс, который считал, что товар всегда где-то есть, должен был начать принимать «мы это ещё не подтвердили» как допустимое состояние.</li></ul>
<p>Именно последний пункт изменил систему сильнее всего, и ему посвящена следующая запись журнала.</p>
<h2 id="section">Почему мы это публикуем</h2>
<p>Мастер-данные склада — самая негламурная часть торговой операции и одна из немногих, от которых зависит всё остальное. Публикация этой записи — способ зафиксировать стандарт, который мы теперь к себе применяем: складская ячейка — это физический факт, а система, которая их выдумывает, хуже системы, которая признаёт, что не знает.</p>
<blockquote class="weour-build-quote">Запись о ячейке, до которой никто не может дойти, — это не данные о запасах. Это украшение.</blockquote>]]></content:encoded>
			<category>Склад</category>
			<category>Запасы</category>
			<category>Качество данных</category>
			<category>WEOUR OS</category>
			<category>Инженерия</category>
		</item>
		<item>
			<title>От 2 магазинов на Ozon к 5</title>
			<link>https://weour.com/ru/build/from-2-ozon-stores-to-5/</link>
			<guid isPermaLink="true">https://weour.com/ru/build/from-2-ozon-stores-to-5/</guid>
			<pubDate>Fri, 11 Sep 2026 00:00:00 +0000</pubDate>
			<description><![CDATA[Архитектура мультимагазина, изоляция магазинов и почему открытие нового магазина не должно создавать вторую копию одних и тех же запасов.]]></description>
			<content:encoded><![CDATA[<p>Мы начинали с двух магазинов на Ozon. Сейчас мы управляем пятью. Интересной частью этого изменения было не количество, а то, сколько допущений позволяли нам сохранять два магазина.</p>
<p>Когда магазинов два, всю картину можно держать в одной голове. Какой листинг к какому магазину относится, какая цена принадлежит какому каналу и что произойдёт, если оба магазина попытаются продать последнюю единицу, — на всё это можно ответить по памяти. На третьем магазине это перестаёт работать, а на пятом ломается очень заметно.</p>
<h2 id="section">Изоляция магазина — это граница, а не копия</h2>
<p>Первая реакция при добавлении магазина — дублировать: скопировать каталог, скопировать остатки, скопировать таблицу mapping и поменять идентификатор магазина. Это выглядит быстро и создаёт вторую версию истины, которая в течение недели разойдётся с первой.</p>
<p>Мы заняли противоположную позицию:</p>
<dl class="weour-build-facts"><div class="weour-build-fact"><dt>Принцип</dt><dd>Что это значит на практике</dd></div><div class="weour-build-fact"><dt>Магазин — это идентичность, а не контейнер остатков</dt><dd>Магазин владеет листингами, ценами и правилами канала, но не собственным реестром запасов</dd></div><div class="weour-build-fact"><dt>Листинг — это артефакт канала</dt><dd>Два магазина могут продавать один и тот же физический товар под разными листингами</dd></div><div class="weour-build-fact"><dt>Идентичность товара общая</dt><dd>Товар описывается один раз, остальные ссылаются на это описание</dd></div><div class="weour-build-fact"><dt>Остатки наблюдаются, а не копируются</dt><dd>Магазин сообщает то, что видит; система фиксирует наблюдение, а не изобретает баланс</dd></div></dl>
<p>Теперь добавление магазина добавляет идентичность канала. Оно не добавляет запасы.</p>
<h2 id="section">Почему «наблюдаемые остатки» вместо баланса по каждому магазину</h2>
<p>Маркетплейсы сообщают данные в своём темпе и своими словами. Один канал отдаёт доступное к продаже количество уже после своих резервов. Другой — то, что ему сообщили вчера вечером. Ни одно из чисел не является ошибочным, и ни одно не является полной правдой о физическом товаре.</p>
<p>Поэтому мы перестали требовать от каждого канала быть источником баланса. Канал сообщает наблюдение: вот что я вижу, из этого источника, в это время. Наблюдения фиксируются, сравниваются и сверяются — они не сливаются молча в одно число, которое невозможно проверить.</p>
<p>Именно это проектное решение делает пять магазинов управляемыми, а не пятикратной нагрузкой на поддержку.</p>
<h2 id="section">Что пришлось построить, чтобы это держалось</h2>
<p>Работа пяти магазинов в одной операционной модели потребовала трёх вещей, которые не были очевидны на двух:</p>
<ul class="weour-build-list"><li>Единая идентичность товара, на которую ссылаются листинги, чтобы один и тот же товар не описывался заново в каждом канале.</li><li>Слой mapping между идентичностями листингов маркетплейсов и нашими SKU, потому что артикул продавца и внутренний идентификатор — не одно и то же.</li><li>Резолвер, который отвечает, «какие каналы могут это продавать и по какой цене», не позволяя ни одному каналу перезаписать общую запись.</li></ul>
<p>Ничего экзотического. Но всё это нужно принудительно поддерживать, потому что сбой здесь тихий: продублированный остаток всё равно выглядит как корректное число.</p>
<blockquote class="weour-build-quote">Стоимость мультимагазина — это не пятый магазин. Это вторая копия истины, которую вы не заметили, что создали.</blockquote>
<h2 id="section">Что меняется по мере добавления магазинов</h2>
<p>Когда изоляция закреплена на границе канала, добавление магазина становится операционной задачей, а не проектом по миграции данных. Следующий магазин выходит в продажу со своим набором листингов, своими правилами цен и своей отчётностью — а товары и физические остатки, на которые он ссылается, остаются там, где были.</p>
<p>Именно это свойство мы и покупали, когда строили модель. Не больше магазинов. Меньше неожиданностей на каждый магазин.</p>]]></content:encoded>
			<category>Мультимагазин</category>
			<category>Ozon</category>
			<category>Маркетплейсы</category>
			<category>Архитектура</category>
			<category>Запасы</category>
		</item>
		<item>
			<title>Мы не купили ещё одну ERP — мы строим Commerce OS</title>
			<link>https://weour.com/ru/build/we-didnt-buy-another-erp/</link>
			<guid isPermaLink="true">https://weour.com/ru/build/we-didnt-buy-another-erp/</guid>
			<pubDate>Sun, 06 Sep 2026 00:00:00 +0000</pubDate>
			<description><![CDATA[Почему WEOUR перестал считать мультиканальную торговлю задачей «купить ещё одну систему» и начал строить операционную систему для глобальной торговли.]]></description>
			<content:encoded><![CDATA[<p>Долгое время вопрос перед нами выглядел как решение о закупке. Каналов продаж становилось больше, товар распределялся между маркетплейсами и складами, а честный ответ на вопрос «сколько у нас на самом деле есть?» зависел от того, кого спросить. Очевидный шаг — купить систему побольше.</p>
<p>Мы попробовали смотреть на это именно так: сравнивали платформы, которые вмещают больше SKU, больше магазинов и больше интеграций. Каждая отвечала на свою версию вопроса. Ни одна не отвечала на наш.</p>
<h2 id="section">Проблема была не в категории софта</h2>
<p>ERP построена вокруг компании, которая уже знает, чем владеет: чистые мастер-данные, одно место, где живёт истина, процессы, которые в основном идут внутри одной организации. Эта модель отлично работает, когда бизнес — это один каталог и один-два канала.</p>
<p>Наша реальность другая. Один и тот же физический товар существует на нескольких маркетплейсах под разными листингами, приходит разными маршрутами поставки и сообщает остатки в разное время в разном формате. Закупка, листинг маркетплейса и складская ячейка — три разных объекта с тремя разными жизненными циклами. Софт, который сжимает их в одну строку, не ошибается — он просто отвечает на более простой вопрос, чем наш.</p>
<h2 id="section">Что мы строим вместо этого</h2>
<p>WEOUR OS — это Commerce OS: набор систем, которые описывают, как связаны товары, листинги, склады и решения, и которые поддерживают эти связи честными, пока бизнес работает.</p>
<dl class="weour-build-facts"><div class="weour-build-fact"><dt>Слой</dt><dd>Ответственность</dd></div><div class="weour-build-fact"><dt>Канал</dt><dd>Каждый магазин сохраняет свою идентичность, правила цен и жизненный цикл листингов</dd></div><div class="weour-build-fact"><dt>Запасы</dt><dd>Наблюдения фиксируются вместе с источником и временем, а не сливаются молча</dd></div><div class="weour-build-fact"><dt>Склад</dt><dd>Физические ячейки моделируются как полноценные записи</dd></div><div class="weour-build-fact"><dt>Mapping</dt><dd>SKU, листинги и идентичность товара связываются, а не считаются одним и тем же</dd></div><div class="weour-build-fact"><dt>Агенты</dt><dd>Автоматизация предлагает и выполняет действия по явным правилам системы</dd></div></dl>
<p>Каждый слой можно заменить. Связи — нельзя.</p>
<h2 id="section">Строим в продакшене, а не в презентации</h2>
<p>Мы публикуем это как журнал разработки, а не как анонс, потому что начинаем не с белой доски. Мы меняем системы, которые уже несут реальные магазины, реальные склады и реальные листинги. Каждое проектное решение должно выдержать проверку живой операцией в тот же день, когда оно принято.</p>
<p>Это ограничение неудобно и полезно. Оно отсекает изящные архитектуры, которые нельзя выкатывать постепенно, и заставляет записать правила, которые бизнес уже соблюдает по умолчанию, но никогда не формулировал.</p>
<h2 id="section">Что это значит для магазина, который вы видите</h2>
<p>weour.com не заменяется новым сайтом. Он и есть одна из систем. Витрина, российский и китайский рыночные каналы, процессы поставщиков и внутренний операционный слой приводятся к одной модели того, чем на самом деле являются магазин, товар и остаток.</p>
<blockquote class="weour-build-quote">Операционная система для глобальной торговли — это не база данных побольше, а меньшее число правил, которым можно доверять.</blockquote>
<h2 id="section">Что дальше</h2>
<p>Следующие записи журнала — о самом сложном: как магазины остаются изолированными, разделяя один каталог; почему новый магазин не должен дублировать запасы; что произошло, когда placeholder-ячейки встретились с реальными; и почему неизвестный остаток никогда не превращается в ноль.</p>
<p>Это и есть то, что мы строим. Мы продолжим публиковать процесс.</p>]]></content:encoded>
			<category>WEOUR OS</category>
			<category>Глобальная торговля</category>
			<category>Архитектура</category>
			<category>Build in Public</category>
		</item>
	</channel>
</rss>
