- Монорепозиторий. Next.js и FastAPI версионируются вместе, но собираются отдельными контейнерами.
- PostgreSQL — единственный обязательный аналитический storage MVP. Интерфейс
AnalyticsSinkоставляет путь к ClickHouse без двойной записи сейчас. - Воспроизводимая демо-база. Seed
20260717генерирует 180 дней метрик, вакансии и пользователей; повторный запуск идемпотентен. - Публичные данные ограничены на сервере. Анонимный/free получает не более 30 дней истории, Premium-профессии возвращают teaser без метрик, ranking ограничен top-3. CSS не участвует в защите.
- Cookie-сессия. JWT хранится в HttpOnly/SameSite=Lax cookie; изменяющие авторизованные запросы используют double-submit CSRF token.
- Демо-платежи не имитируют реальный acquiring.
DemoPaymentProviderсоздаёт проверяемые идемпотентные события. Реальный provider добавляется через интерфейс. - HH выключен по умолчанию. Одновременно обязательны feature flag, письменно подтверждённое право коммерческого использования, app name и contact email. Используется только официальный API, без HTML, CAPTCHA/proxy/rate-limit обходов.
- AI необязателен. Объяснимый rule-based classifier — основной. Ollama может разбирать только неопределённые записи через отдельный профиль/флаг; GPU не требуется.
- Достоверность важнее красивого числа. Midpoint рассчитывается только при двух границах, gross/net группируются раздельно, минимум по умолчанию — 20 наблюдений.
- Ресурсный бюджет. Compose-лимиты суммарно ниже 8 ГБ в local-dev и не резервируют GPU.
- Проверки в текущей машине. Системные
node,dockerиollamaпервоначально не обнаружены в PATH; для Node/Python используются runtime Codex, фактический Docker-запуск зависит от доступности Docker Desktop. - Локальная Ollama проверена 2026-07-17. API
127.0.0.1:11434доступен, установленаmistral:7b(~4,37 ГБ), inference завершился. Однако модель не выполнила простую constrained-инструкцию и сгенерировала нерелевантный текст; её нельзя включать в классификацию без отдельного quality benchmark, JSON-schema validation и rule-based confidence gate. - Demo-аккаунты используют
example.com. Адреса@demo.localотклоняются сервернымEmailStr, поэтому воспроизводимые пользователи имеют emailfree@example.com,premium@example.comиadmin@example.com. Для уже созданной локальной demo-базы успешный вход по новому адресу безопасно и однократно мигрирует соответствующую legacy-запись. - Первоначальный mailto-подход личного ведения заменён. Ранний MVP открывал почтовое приложение посетителя; решение №29 заменяет его отдельной серверной формой по явному запросу владельца.
- Происхождение данных раскрывается без навязчивой маркировки каждого экрана. Главные продуктовые страницы используют нейтральные статусы работы сервиса, а описание подготовленного набора, провайдеров и ограничений остаётся на страницах «Источники» и «Методология», в документации и машинных описаниях. Значение
liveдопустимо только после успешной загрузки законного официального источника, проверки условий использования и сохранения provenance; свежая дата сама по себе не превращает подготовленные данные в реальные. - Стек профессии является публичным справочным контентом. Для всех 50 slug хранится отдельный набор языков, фреймворков, программ и платформ. Он доступен даже на Premium-teaser странице, потому что не содержит платных метрик и улучшает полезность индексируемой landing page.
- Недельное изменение в рейтинге означает динамику спроса, а не смену номера строки. Сравниваются средние количества вакансий за два соседних семидневных окна по России; это позволяет показать воспроизводимый процент и стрелку без создания фиктивной истории score.
- Машиночитаемость не гарантирует позиции или цитирование. Публичные страницы содержат JSON-LD
Occupation,Dataset,WebSite,Organization, canonical URL, sitemap и/llms.txt. Premium-значения в эти представления не добавляются; решение об индексации и цитировании остаётся за поисковой системой или LLM. - Референсы используются только как паттерны взаимодействия. В редизайне объединены последовательная анимация слоёв, кинематографичная иерархия и jobs-first поиск, но не копируются изображения, логотипы, тексты, шрифты, фирменная композиция или товарные знаки Apple, Netflix и hh.ru.
- Карьерная сцена является оригинальной и code-native. После недоступности генератора изображений персонаж, деньги, пиджак и сумка реализованы собственными HTML/CSS-слоями без внешних ассетов. Hover имеет эквивалент через focus и tap, а
prefers-reduced-motionпоказывает статичную композицию. - Состояния карьерной сцены должны читаться без инструкции. Начальное состояние показывает сутулую посадку, худи, кресло и обеспокоенное выражение; итоговое - прямую осанку, рубашку, галстук, пиджак, рабочую сумку и позитивное выражение. Подписи «наведите» и «интерактивная сцена» убраны как избыточные.
- Светлая тема меняет не только токены карточек. В ней осветляются шапка и главный экран, усиливается белый фон и снижается визуальный вес теней; тёмная тема сохраняет кинематографичную чёрно-графитовую подачу.
- Dev и production используют разные каталоги Next.js. Compose задаёт
NEXT_DIST_DIR=.next-dev, а production build продолжает собираться в.next. Это исключает повреждение dev route manifest после локальной production-проверки и предотвращает массовый 404 динамических страниц/professions/[slug]. - Техподдержка отделена от формы личного ведения.
/supportотправляет заявку в собственный backend-контур: CSRF и Origin check, Redis rate limit, PostgreSQL, Celery и заменяемыйEmailProvider. Если SMTP временно не настроен, заявка не теряется и остаётся в статусеpending_configuration; фактическая доставка включается только секретом окружения. - Почтовые учётные данные не автоматизируются через интерфейс аккаунта. Получатель фиксируется конфигурацией, а SMTP использует отдельный пароль приложения. Обычный пароль пользователя не запрашивается сайтом и не хранится в репозитории.
- Максимальная локальная модель отделена от критического пути сайта. На RTX 3070 Ti 8 ГБ + 32 ГБ RAM установлена
qwen3.6:27bQ4_K_M (17 ГБ). Она прошла короткую 4K structured-output проверку, но работала примерно 70% CPU / 30% GPU, заняла почти всю RAM/VRAM и выдала около 1,46 токена/с после холодной загрузки. После отдельной Docker-provider проверки AI-assist разрешён только для максимум трёх неопределённых записей в ночном запуске, со schema validation и confidence cap; критический путь остаётся rule-based. - Установка модели не означает её постоянную загрузку. После benchmark выполнен
ollama stop qwen3.6:27b: RAM и VRAM освобождены, Docker-сервисы продолжают работать независимо, а 17-гигабайтная модель остаётся на диске. - CSP различает development и production. Next.js development получает
unsafe-eval, необходимый только React dev-tools для отладочных стеков; production build его не добавляет. Остальные ограничения CSP одинаковы, а браузерный smoke после изменения не показывает console errors. - Поддержка и личное ведение являются двумя разными функциями. У них отдельные API, CSRF cookies, таблицы (
support_requests,mentorship_requests), Celery tasks и темы писем[TechRole Support]/[TechRole Mentorship]. Они разделяют только проверенный SMTP provider и антиспам-инфраструктуру. - Неудачная SMTP-аутентификация автоматически не повторяется бесконечно. После первоначального ответа Яндекса
535заявки были сохранены без циклических попыток. После включения доступа почтовых программ 2026-07-19 SMTP активирован: Support, Mentorship и Nightly доставлены с разными темами, сохранённые заявки повторно отправлены, наличие пяти писем подтверждено через IMAP. Секрет остаётся только в локальном.env. - Черновой реальный контур использует только официальный open-data API «Работа России».
TrudvsemOpenDataProviderне скрейпит HTML, ограничивает число запросов и сохраняет только allowlist без контактов/адресов. HH по-прежнему выключен и не вызывался. - Неизвестный gross/net не смешивается с публичными числами. Реальные записи «Работы России» сохраняются как provenance и salary observations с
gross = unknown; nightly job намеренно не перезаписывает текущую gross-витрину до появления корректного совместимого преобразования. - Dagster отвечает за рыночный nightly orchestration. Schedule
techrole_midnight_moscowработает в00:00 Europe/Moscow, имеет persistent run history, отдельный webserver/daemon и email-отчёт. Celery остаётся очередью форм сайта и прикладных задач. - Qwen подключена только как ограниченный AI-assist. Docker успешно вызвал
qwen3.6:27bчерезhost.docker.internal; модель возвращает только разрешённые slug/seniority, имеет confidence cap 0.79, максимум три вызова на nightly-run и выгружается после обработки. Rule-based результат остаётся главным. - LLM-доступность строится на открытых стандартах, а не скрытом тексте. Публичные SSR-страницы дополнены
llms.txt,llms-full.txt,ai-index.json,Occupation/Dataset/DataCatalog/FAQPageJSON-LD, glossary, editorial policy, sitemap и явными crawler rules. Эти меры улучшают извлекаемость и корректное цитирование, но не гарантируют выдачу конкретной LLM. - Chromium не входит в обычный frontend runtime. Playwright запускается отдельным Compose-профилем на официальном version-matched образе; зависимости и результаты живут в отдельных volumes. Внутренний hostname
frontendразрешён только как Next development origin, production security policy не ослабляется. - «Работа России» не считается зеркалом hh.ru. Используется самостоятельный официальный open-data API портала Роструда. Source конкретной записи сохраняется, но запись не приписывается hh.ru без проверяемого provenance.
- Официальный 180-дневный слой измеряет публикации. API не предоставляет достаточной истории статуса для восстановления одновременно активных вакансий. Поэтому серия строится по
creation-date, старые пробные записи вне окна удалены, а UI и JSON-LD содержат явную оговорку. - Реальные грязные данные не должны останавливать ingestion. Длинные TSV-фрагменты в
job-nameочищаются до первой ячейки и лимита базы с сохранением оригинала вraw_payload; навыки дедуплицируются по фактически сохраняемым 120 символам. Оба случая покрыты тестами. - Удалённый доступ не публикует RDP в интернете. Tailscale установлен на ПК и iPhone, NLA включена, firewall разрешает 3389 только на Tailscale-интерфейсе из
100.64.0.0/10. Сценарий теперь перезапускает TermService и проверяет listener; после изменения требуется один повторный запуск от администратора. Сон/гибернация ПК от сети отключены. - Машиночитаемые страницы являются открытыми, но не обходят paywall.
open-data.json,ai-index.json, RSS,llms.txtи публичный JSON-LD содержат открытые описания, агрегаты публикаций и отдельные зарплатные вилки официального источника с неизвестным gross/net. Premium gross-метрики, score breakdown и длинная история туда не включаются. - Официальная зарплата публикуется отдельным контуром. Для «Работы России» midpoint, median, average и P25/P75 считаются только по RUB-записям с двумя границами и отдельно для Junior/Middle/Senior. Первоначальный порог
MIN_SALARY_SAMPLE=20заменён решением 122; принцип «не публиковать ноль или синтетическое число вместо отсутствующего среза» сохраняется. - История официальной зарплаты использует устойчивые окна. Точки строятся раз в семь дней по предшествующему 30-дневному окну. Это снижает шум редких ежедневных публикаций и не смешивает неизвестный налоговый статус с gross-витриной.
- Незавершённый ingestion восстанавливается как ошибка. При следующем запуске состояния
runningстарше часа переводятся вfailedс причинойinterrupted_before_completion; два следа прерванных перезагрузкой запусков 2026-07-19 исправлены в локальной БД. - Свободный HTTPS не равен production-развёртыванию. Tailscale Funnel доступен на всех тарифах и даёт HTTPS
*.ts.net, но остаётся beta, зависит от домашнего ПК и не заменяет стабильный домен, production-секреты и мониторинг. Dev-сервер публично не открыт. - Поисковая регистрация выполняется после появления канонического публичного host. Sitemap, robots, RSS, JSON-LD,
/.well-known/llms.txtи IndexNow-compatible данные подготовлены. Отправлять localhost или временный URL в Search Console/Webmaster бессмысленно; внешние ссылки не создаются спамом. - Git baseline исключает локальные зависимости и runtime. Репозиторий инициализирован на ветке
main;.env,.pnpm-store,node_modules, Next dist, TypeScript cache, Celery schedule и PostgreSQL backups не отслеживаются. Первый commit фиксирует проверенное исходное состояние до продолжения разработки. - Публичный preview использует только production standalone. Dev-сервер не открывается наружу.
public-frontendработает на loopback 3100, получает backend через внутренний Compose DNS и считается healthy только при наличии 50 сущностей вai-index.json. Новая сборка создаётся в отдельном.next-public-live, чтобы не обрывать туннель на время компиляции. - Для AI-поиска не создаётся псевдоспециальная разметка. Официальные рекомендации Google сводят AI-функции к обычным требованиям индексируемости, полезному видимому тексту, внутренним ссылкам и согласованным structured data.
llms.txtостаётся дополнительным открытым указателем, а не обещанием индексации. - Цитирование поддерживается переносимыми стандартами. Публичная страница
/citationсвязана с CSL-JSON, BibTeX, RIS, Frictionless Data Package иLink-отношениями open-data ответа. Форматы используют один canonical host, дату ingestion, методологию и provenance; Premium-поля в них не добавляются. - Исследовательский обзор рассчитывается из открытого API.
/researchи/research.jsonдинамически суммируют публикации, роли с данными, честные нули, salary-ready выборки и топ профессий. Текст явно называет показатель публикациями по creation date, а не одновременно активными вакансиями. - Auth rate limit хранится в Redis. Login и register разделяют окно 10 попыток на HMAC-хеш IP за 10 минут между backend workers. Превышение возвращает 429; недоступность Redis в production закрывает вход с 503, а не включает бесконтрольный fail-open.
- Официальные валютные курсы отделены от demo-нормализации.
CbrCurrencyRateProviderчитает датированный XML только с HTTPS-host Банка России, сохраняет requested/effective date и нормализует номинал. Миграция0004добавляет идемпотентные snapshots USD/EUR/KZT; отдельный Dagster op работает только приCBR_CURRENCY_ENABLED=true. Это ещё не разрешает смешивать неизвестный gross/net с gross-витриной. - IndexNow защищён от временных host. Key-файл появляется только из server-side
INDEXNOW_KEY; submit-скрипт сверяет его с публичным host, берёт URL только из sitemap и отклоняет localhost,*.lhr.lifeи*.trycloudflare.com. - CI и backup являются частью baseline эксплуатации. GitHub Actions выполняет backend, frontend, Compose и Chromium E2E, Dependabot готов для Python/npm/actions. Локальный PostgreSQL custom backup получает SHA-256 manifest, проверку
pg_restore --listи retention, но не выдаётся за шифрованную off-host копию. - Минимальная observability не требует передачи пользовательских данных. Backend принимает только безопасный
X-Request-IDили генерирует UUID, возвращает его клиенту и пишет JSON log с method/path/status/duration. Query string, IP, email и body не логируются; health checks исключены из обычного access log. - Machine-specific remote-access данные не входят в tracked head. Tailscale IP, MagicDNS и Windows username перенесены в игнорируемый
LOCAL_OPERATIONS.md. Старый baseline commit всё ещё содержит прежнюю документацию, поэтому публичный remote должен начинаться с нового очищенного history, а не с публикации текущей истории как есть. - Статус слоя является частью публичного контракта.
/data-status,/data-status.jsonи страницы профессий называют базовую gross-витринуprepared_baseline, официальный 180-дневный ряд -observed_historical, аcurrent_market_claimдля обоих равенfalse. Дата подготовленной метрики не выдаётся за проверенную свежесть рынка; публикация не выдаётся за одновременно активную вакансию. - Открытый CSV имеет длинный seniority-формат.
/open-data.csvсоздаёт по строке на сочетание профессия × Junior/Middle/Senior и обязательно включает период, n, confidence, currency, tax status, canonical URL, source и methodology. Отсутствующее значение остаётся пустым, честный ноль остаётся нулём, текстовые formula-префиксы нейтрализуются для spreadsheet-клиентов. - Метрики имеют ограниченную cardinality и не несут пользовательские данные. Внутренний
/metricsэкспортирует Prometheus counter/histogram только с labels method, восстановленный route template и status class. Фактический slug/path, query, IP, request ID, email, cookie и body исключены; неизвестный маршрут получает labelunmatched. Два production workers используют multiprocess directory в tmpfs и Gunicorn cleanup hook. Optional Prometheus profile хранит 15 дней на локальном volume и содержит alerts для down/5xx/p95, но внешняя доставка без выбранного Alertmanager-канала не включается. - Cache не является границей доступа. Catalog/detail кэшируются только после расчёта entitlement; key parts содержат отдельный
public/premiumtier и фактически разрешённую глубину истории, затем целиком хешируются SHA-256. TTL равен 120 секундам. Redis failure для cache работает fail-open к PostgreSQL, но не меняет fail-closed политику auth rate limit; hit/miss/error имеют только bounded Prometheus labels. - Временный туннель подключается к постоянному local proxy, а не к пересоздаваемому standalone.
public-proxyдержит loopback listener 3199 и проксирует в production frontend на 3100. Переключение immutable slot пересоздаёт только upstream с--no-deps, поэтому SSH не теряет локальный origin. Это не гарантирует hostname: анонимный localhost.run может выдать новый URL внутри того же PID, поэтому refresh-скрипт выбирает последнюю запись лога и хранит previous URLs. Proxy удаляет client-IP forwarding headers, имеет независимый noindex health endpoint и не используется как замена production ingress. - Editorial growth ограничен самостоятельными проверяемыми материалами.
/insightsпубликует двенадцать уникальных разборов из утверждённого backlog, каждый имеет четыре и более смысловых раздела, checklist, ссылки на основание, citation text, canonical иTechArticleJSON-LD. Полный текст доступен вinsights.json, summaries - в RSS/AI/LLM indexes. Короткие doorway-вариации и массовое размножение одинакового текста не создаются. - Доступность и crawlability являются проверяемыми контрактами. Playwright проверяет representative public pages на один
main/h1, уникальные id, подписи form controls, имена интерактивных элементов, alt-атрибуты, keyboard skip-link, отсутствие mobile overflow и фактическое подавление повторяющейся анимации приprefers-reduced-motion. Отдельный dependency-free crawl требует не менее 60 sitemap URL и проверяет каждый canonical HTML URL: HTTP/content type, уникальные title/description, canonical,lang=ru, один h1, отсутствиеnoindexи синтаксис всего JSON-LD. Временные network/429/5xx ошибки получают максимум пять bounded attempts. Оба слоя запускаются в CI; результат не считается полевым Core Web Vitals замером. - Standalone считается готовым только вместе с browser assets. Next
output: standaloneне копирует<dist>/staticи optionalpublicавтоматически.prepare:standaloneпереносит их внутрь immutable bundle с учётом customNEXT_DIST_DIR, ограничивает путь frontend-каталогом и проверяет ненулевой asset count. Внешний smoke запрашивает все CSS/JS главной страницы; один только HTTP 200 HTML больше не считается достаточным deployment check. - Lab performance budgets являются regression guardrail, а не полевыми Core Web Vitals. На прогретой повторной навигации representative страниц Playwright требует TTFB ≤ 2000 мс, FCP ≤ 3000 мс, LCP ≤ 4000 мс, CLS ≤ 0.1 и event duration темы ≤ 300 мс. Метрики прикладываются к test result и запускаются в общем E2E profile. Они ловят отсутствующий CSS/hydration и крупные layout shifts, но реальные p75 LCP/CLS/INP после стабильного домена всё равно должны измеряться по RUM/Search Console.
- Утверждённый editorial backlog закрывается глубиной, а не количеством URL. Добавлены самостоятельные материалы о 180-дневном ряде, seniority-сигналах, нулевых совпадениях узких ролей, индексе 0–100, server-side paywall и LLM-friendly публикации. Вместе с первыми шестью это покрывает все 12 тем backlog. Новая тема появляется только при отдельном проверяемом вопросе; программное размножение комбинаций профессия×город×уровень не допускается.
- Каждая editorial статья цитируется как самостоятельный Web resource. Article page объявляет Highwire-style citation meta и MIME-specific alternates, видимо связывает CSL-JSON, BibTeX и RIS, а
insights.json/ai-index.jsonпубликуют те же URL. CSL type равенwebpage, author/publisher — TechRole Index, issued берётся изpublishedAt, URL всегда указывает на canonical Article page. Extensionless/cite/*paths выбраны после live-проверки: Next 16 непоследовательно регистрировал вложенные dynamic folders с.json/.risв имени. - Observed publication transforms не используют prepared metric table. Миграция
0005создаётobserved_publication_metrics_dailyс source/date/profession/seniority/region/tax-status lineage. PostgreSQL refresh группируетpublished_atв UTC, а не snapshot active-state, делает idempotent upsert с run-token, удаляет stale slices в той же транзакции и проверяет суммы/count/coverage до commit.gross,netиunknownразделены; зарплатные значения ниже sample gate остаются NULL. Dagster запускает materialization только после полностью успешного ingestion. - Daily export публикует агрегаты, а не provider payload.
/open-data-daily.jsonи/open-data-daily.csvберут только строкиobserved_publication_metrics_daily, добавляют source/field dictionary/methodology/citation/transform version и в каждой записи сохраняютcurrent_market_claim=false. Raw payload, пользовательские данные и Premium prepared metrics отсутствуют. API передаёт фактическийsalary_minimum_sample; недоступность backend не маскируется пустым набором, а даёт503,Retry-Afterиno-store. CSV различает пустое значение и наблюдённый ноль и нейтрализует formula-префиксы, в том числе после начальных пробельных и управляющих символов. - HTML-страница набора является описанием, а не копией 691 строк. Канонический
/open-data-dailyпоказывает grain, период, агрегаты, полный словарь 27 JSON-полей, ограничения интерпретации и ссылки на JSON/CSV; сами записи остаются в переносимых форматах. Dataset JSON-LD повторяет только видимое и проверяемое, указывает применимые к исходному государственному набору типовые условия открытых данных без самовольной перелицензии в CC BY и сохраняетcurrent_market_claim=false. Summary вычисляется независимо от порядка строк, а machine endpoints связываются со страницей черезdescribedby. - Публичное 180-дневное окно состоит из полных UTC-дней. Research catalog и data provenance используют один helper и полуоткрытый интервал
[00:00 date_from, 00:00 после date_to). Текущая секунда больше не отрезает начало первого дня, аnow + 1 dayне захватывает записи следующего календарного дня. Provenance schema1.2явно публикуетUTC_calendar_days, начало и exclusive end; накопительная materialized history при этом может быть шире текущего rolling window и сохраняет собственный диапазон. - Daily dataset имеет исполняемый публичный контракт.
/open-data-daily.schema.jsonпубликует строгий JSON Schema Draft 2020-12: запрещает неизвестные свойства, требует все 27 полей записи, фиксирует допустимые enum/const, null-safe зарплатные значения, диапазоны долей иcurrent_market_claim=false. JSON export сам ссылается на schema; HTML, Data Package, citation, AI/LLM indexes и provenance дают тот же canonical URL. Unit, Playwright и внешний smoke сверяют контракт с каждой фактической строкой, поэтому документация не может незаметно разойтись с выдачей. - Публичные daily representations поддерживают стандартную условную загрузку. JSON, CSV и JSON Schema получают strong SHA-256
ETagпо фактическим байтам; изменяемые JSON/CSV дополнительно публикуют одинаковыйLast-Modifiedпо последней материализации.If-None-Matchиспользует weak comparison для GET, понимает список и*и имеет приоритет надIf-Modified-Since; совпадение возвращает пустой304с cache/link validators. Заголовки открыты через CORS. Ошибки backend остаются503,no-storeи без ETag, чтобы недоступность не кэшировалась как датасет. - Production-конфигурация fail closed и не переиспользует demo volume. Settings отклоняет production с demo mode, localhost/HTTP origin, известным/коротким secret или default/коротким PostgreSQL password. Compose получает согласованные
POSTGRES_*/DATABASE_URL, передаёт canonical URL в frontend build и runtime, передаётSITE_ADDRESSCaddy, очищает base ports/source mounts через!reset/!overrideи оставляет наружу только 80/443. Rendered JSON проверяется отдельным dependency-free валидатором в CI. На пустой БД seed создаёт только reference catalog; существующие demo accounts или enabled demo source блокируют production bootstrap вместо молчаливой публикации synthetic baseline. - Читаемый backup ещё не считается проверенным восстановлением. Backup script использует фактические
POSTGRES_USER/POSTGRES_DBи уникальный container path. Отдельный restore-test сверяет manifest SHA-256, разворачивает custom archive только в БД с генерируемым префиксомtechrole_restore_, проверяет Alembic revision, каталог и ключевые таблицы, затем удаляет временную БД. Основная БД не очищается и сервисы не останавливаются. - Бесплатный публичный адрес разделён с production-host. Tailscale Funnel публикует только loopback production-proxy
127.0.0.1:3199и даёт стабильный preview hostname, но остаётся beta, имеет bandwidth limit и зависит от Windows-host. OCI Always Free Ampere A1 выбран как возможный$0host полного Compose после регистрации владельца; Render/Koyeb free отвергнуты для основной системы из-за истечения/нестойкости БД, scale-to-zero, недостатка ресурсов и отсутствия worker/persistent volume. Cloudflare named Tunnel предпочтителен после появления собственного домена. - Daily dataset публикуется в Croissant 1.1 как live dataset.
/open-data-daily.croissant.jsonиспользует профильhttp://mlcommons.org/croissant/1.1, описывает все 30 фактических CSV-колонок, их типы и составной ключ, связывает source/provenance/citation и намеренно не фиксирует SHA-256 изменяемого CSV. В HTML встраивается та же JSON-LD модель; AI/LLM indexes, robots, Data Package иLinkheaders ведут на неё.licenseссылается только на федеральные типовые условия, на которые указывает «Работа России»; отдельная лицензия кода или дополнительных прав владельца требует самостоятельного решения. Официальныйmlcroissant 1.1.0обязан успешно выполнить и validate, и загрузку пробных записей. - Публичный Git remote проверяется по всей истории до push. Gitleaks сканирует все commits с redaction. Единственное старое совпадение — синтетический
app_secret_keyиз production-settings test — исключается экспериментальным механизмом.gitleaksignoreтолько по полному fingerprint публичного commit/path/rule/line; целые commits, пути и rules не разрешаются. Staged diff и вся очищеннаяpublic-mainдаютno leaks found; отдельный SHA-pinned GitHub workflow повторяет проверку на push, PR и еженедельно. - Каталог данных дополнительно публикуется в W3C DCAT 3.
/catalog.jsonldописывает daily dataset отдельно от его JSON/CSV distributions, указывает прямые download URL, DataService/OpenAPI, temporal coverage, источник, федеральные условия и Croissant/JSON Schema conformance. Endpoint использует тот же materialized observed layer, conditional SHA-256 ETag и fail-closed503; Data Package, HTML, robots и AI/LLM indexes связывают каталог без создания дублирующей landing page. - Next dev assets разрешены только ожидаемым локальным origin.
allowedDevOriginsвключает loopback127.0.0.1,localhostи Compose hostnamefrontend, чтобы локальный и контейнерный Playwright получали hydration/HMR вместо server-only HTML. Настройка применяется только к dev-resource protection; production CSP и публичный proxy не ослабляются. - CSV получает стандартное W3C CSVW-описание по discoverable URL.
/open-data-daily.csv-metadata.jsonследует стандартному правилу<csv-url>-metadata.json, использует обязательный CSVW context, описывает все 30 фактических колонок, их datatypes/nullability и составной primary key. CSV связывает metadata последним подходящимLink: rel="describedby"; type="application/csvm+json", а metadata обратно объявляетrel="describes". Column definitions разделяются с Croissant, чтобы два контракта не расходились. - Canonical Dataset JSON-LD является центром, а форматы — его документацией. Landing публикует стабильный identifier, alternate name, creator/publisher, datePublished, spatial/temporal coverage и keywords. JSON Schema, CSVW, Croissant и DCAT связаны через
subjectOf, а неsameAs: они описывают или представляют один набор, но не являются четырьмя независимыми каноническими копиями. - Schema.org
Dataset.citationне используется для самоцитирования набора. По контракту Google Dataset Search это поле предназначено для связанной академической публикации. Сам набор идентифицируютname,identifier,creator,publisherи canonicalurl; человеко- и машиночитаемые форматы цитирования остаются на/citation, но не маскируются под стороннюю научную работу. - CFF-разметка проверяется по схеме, а не только визуально. Верхнеуровневый GitHub-тип остаётся
dataset, ноpreferred-citation.typeиспользует разрешённое CFF 1.2 значениеdata. Файл обязан проходитьcffconvert 2.0.0 --validateдо публикации remote и после добавления постоянных repository/DOI identifiers. - AI-assist получает двойной structured-output guard и измеримый domain gate. Ollama принимает JSON Schema с enum всех 50 slug,
additionalProperties=false, nullable seniority и confidence 0..1; Pydantic повторно валидирует фактический ответ, после чего прежний cap 0.79 остаётся обязательным. Проверяемый 20-case synthetic benchmark требует 100% schema validity/abstention и пороги по slug/seniority/exact, выгружает модель вfinallyи не содержит provider payload. Результат20/20не заменяет будущую размеченную holdout-выборку и не делает AI критическим путём. - Backend tests не наследуют mutable Compose
.env.conftest.pyдо импорта приложения принудительно задаёт test app env, SQLite, localhost origins и отключает cache/AI/HH/Trudvsem/CBR/email integrations.setdefaultдля критических настроек недостаточен: он позволял Compose PostgreSQL и временным public origins менять смысл unit-тестов и вызывать ложный CSRF403. - Любой публичный tunnel завершается на одном production-like proxy. И Tailscale Funnel, и Cloudflare Quick Tunnel направляются только на loopback
127.0.0.1:3199, где frontend и разрешённые/api/*маршруты объединены одним ingress. Прямой quick tunnel на standalone frontend3100был бы неполным: health/API smoke и формы снаружи не работали бы. - Фактический Funnel status важнее exit code команды настройки. После one-time approval CLI может одновременно настроить background Funnel и вернуть ненулевой код исходному ожидавшему процессу. Скрипт считает запуск успешным только если
tailscale funnel status --jsonподтверждает включённый*.ts.nethost и точный root proxy на127.0.0.1:3199; текстовый баннер и один exit code не являются достаточной проверкой. - Эксплуатируемые уязвимости раскрываются приватно. Security-проблемы не направляются в публичный issue вместе с proof of concept или данными. Основной канал после публикации — GitHub Private Vulnerability Reporting, резервный — отдельная тема письма владельцу; data-quality сообщения без security-влияния остаются публичными и используют отдельный шаблон.
- Первый публичный snapshot имеет версию
0.1.0. Backend/frontend manifests, CFF и changelog используют одну версию и дату 2026-07-21. Release notes фиксируют проверенные contracts и известные ограничения; динамический период/число наблюдений остаются свойствами dataset provenance, а не SemVer приложения. Ссылки changelog получают фактического GitHub owner до публикации. - После canonical cutover временный tunnel закрывается по проверенной identity. Stop-сценарий не завершает процесс только по сохранённому PID: он дополнительно требует точного полного пути
%LOCALAPPDATA%\TechRoleIndex\bin\cloudflared.exe, затем отмечает runtime status остановленным. Tailscale Funnel и production proxy при этом не изменяются. - Все сторонние GitHub Actions закреплены на commit SHA. Официальные major refs проверены через GitHub API 2026-07-21 и заменены неизменяемыми digest с читаемым major-комментарием. Dependabot остаётся каналом обновления digest; плавающий
@v4/@v5больше не исполняется в CI с доступом к repository token. - Связи dataset публикуются стандартным RFC 9264 Linkset. Landing обнаруживает
/.well-known/linkset.jsonчерез HTTPrel="linkset"; документ связывает canonical dataset, JSON/CSV content resources, JSON Schema, CSVW, Croissant, DCAT, Data Package, CSL и provenance.cite-asуказывает на сам стабильный canonical landing, а не выдаёт инструкцию/citationза persistent identifier. Linkset получает registered media type, CORS, strong ETag и условный304. - Статические Article pages и вложенные citation handlers имеют разные dynamic semantics.
dynamicParams=falseсохраняется у самой страницы для SSG только 12 утверждённых slugs. Три вложенных route handler явно разрешают dynamic params и сами проверяют slug черезgetInsight; иначе свежий Next dev cache возвращал404даже для существующей статьи, хотя её page была сгенерирована. - Hosted monitor повторяет полный внешний contract smoke раз в сутки. GitHub Actions с минимальным
contents: readзапускает существующий PowerShell-сценарий на Ubuntu runner против canonical Funnel URL; manual dispatch разрешает другой HTTPS origin. Красный run при выключенном Windows-host считается честным сигналом host-dependent preview, а не дефектом dataset contract. - ЮKassa — основной платёжный адаптер, Robokassa — резерв. Решение принято по официальным материалам на 2026-07-21: у ЮKassa зрелый test shop/REST/idempotency/refund/autopayment contract, а подлинность неподписанного webhook безопасно устанавливается авторизованным чтением объекта. Для владельца на НПД выбор повторно проверяется до live: ЮKassa больше не автоматизирует чеки самозанятых, тогда как Robokassa публично предлагает Roboчеки.
- Browser никогда не определяет сумму или статус заказа. Он передаёт только allowlisted product code, текущую версию условий и факт явного принятия. Amount/currency/description/access duration создаёт серверный catalog; любой webhook повторно сверяется с локальным order, test/live mode и provider object.
- Webhook authenticity зависит от провайдера, но fail closed одинакова. Demo использует HMAC raw body и constant-time compare. ЮKassa не подписывает уведомления, поэтому тело считается лишь сигналом: backend с Basic auth запрашивает текущий payment/refund по id и обрабатывает только ответ API. Payload событий хранится без карточных/персональных данных, повторы дедуплицируются уникальным provider event id.
- Реальные списания требуют нескольких независимых guard. Одного production key недостаточно: обязательны отдельные
PAYMENTS_LIVE_CONFIRMED,PAYMENTS_LEGAL_APPROVED, утверждённая не-draft версия оферты, статус продавца, совместимая фискализация и при онлайн-кассе явный актуальный VAT code. НПД и ИП/компания не могут использовать одну и ту же фиктивную кассовую настройку. - Tailscale Funnel остаётся sandbox-preview, а не платёжным hosting. Он доступен по HTTPS, но webhook зависит от включённого ПК и локального Docker. Live требует постоянного домена/host, мониторинга и резервного восстановления; до этого UI обязан явно маркировать оплату тестовой и не запрашивать реальные реквизиты.
- Фактические доходы специалистов являются третьим слоем данных. Публичные исследования не заполняют NULL в vacancy metrics и не участвуют в score. Профессия получает точный срез, затем технологический/смежный, а при отсутствии — явно категорийный fallback; P10/P90 не интерпретируются как Junior/Senior.
- Главный salary benchmark зафиксирован на отчёте Хабр Карьеры I полугодия 2026. Публичный срез
n=45 226хранит период, source URL, методологию, net basis иsalary_plus_bonus. Для грейдов допускаются дополнительные публичные источники, но их малыеn, unknown tax status и замечания об аномалиях обязаны оставаться видимыми. - Provenance schema
1.3публикует три несмешиваемых слоя.prepared_analytics,official_publicationsиsalary_benchmarksимеют собственные статусы иcurrent_market_claim=false. Для 50 профессий контракт фиксирует покрытие: direct, related или category-only; обновление чисел требует изменения версионированного source dataset и тестов полного покрытия. rules-v2расширяет покрытие только однозначными сигналами. Skills участвуют лишь для обобщённого software-заголовка и только когда после подавления специфичных ролей остаётся одна профессия; явный title alias имеет приоритет, неоднозначность означает abstention. Повторная классификация затрагивает только null/rules-*, поэтому две AI-классификации сохранены. На текущей БД rule-managed покрытие выросло с 1 050 до 1 130 строк без очисток, а число представленных профессий — с 33 до 36.- Подтверждённый НПД меняет основной платёжный вариант на Robokassa. Решение 97 сохраняет историю выбора до ответа владельца, но с 2026-07-22 его supersede-ит Robokassa: официально поддерживаются самозанятые без ИП и автоматические «Робочеки СМЗ». ЮKassa остаётся резервом с сильным REST contract, но её автоматизация чеков НПД прекращена. Публичный калькулятор Robokassa на дату проверки показывал для стартового НПД карты РФ 3,9% и СБП 2,7%; договор в кабинете всегда приоритетнее.
- Robokassa ResultURL считается подлинным только после полной серверной сверки. Browser передаёт лишь product code; сервер формирует сумму, описание, чек, числовой
InvId, Success/Fail URL иShp_order_id, подписывает Паролем №1, а ResultURL принимает form-urlencoded POST только с корректной constant-time подписью Паролем №2. Сумма, order/payment id и test/live режим повторно сверяются с БД, повторы дедуплицируются, а успешный запрос всегда получает требуемыйOK<InvId>. - Возврат Robokassa в test и live имеет разные проверяемые контуры. Официальный
OpStateExtне видит test payments, поэтому безденежные возвраты остаются в demo и MockTransport contract tests. Live сначала получает подписанныйOpKey, затем отправляет HS256 JWT по отдельному Password3; pending-запросы сверяются фоново, и entitlement отзывается только после подтверждённогоfinished. Production принимает только официальные API endpoints, чтобы секреты нельзя было вывести через изменённую конфигурацию. - Постоянный host выбирается по воспроизводимому бюджету, а домен — только на владельца. На 2026-07-22
techrole.ruбыл свободен; официальные страницы показывали.ruза 200 ₽ с продлением 399 ₽ у Timeweb и 199/420 ₽ у Beget. Основной экономный вариант — Timeweb Cloud 2 vCPU/4 ГБ/50 ГБ от 721 ₽/месяц; Funnel остаётся preview. Покупка домена/аккаунта через FunPay отвергнута из-за отсутствия подтверждённого выигрыша и риска чужой регистрационной ownership. - Публичная SEO-медиана допустима, скрытые данные калькулятора — нет. Для семи точных ролей Хабр Карьера открыто публикует медиану общего дохода в title/description отфильтрованной страницы. Снимок 2026-07-22 хранит только это число и прямую ссылку; значения распределений/грейдов, скрытые входом, не извлекаются. Поскольку публичная методология не подтверждает для этих страниц
nи gross/net, оба поля остаются неизвестными. Покрытие меняется с 29/12/9 на 36 прямых, 12 смежных и 2 только категорийных роли без влияния на vacancy metrics или score. - Датированный зарплатный снимок проверяется, но не обновляется автоматически. Точная публичная медиана ручного тестирования расширяет покрытие до 37/11/2. Опциональный Dagster op делает максимум три ограниченные попытки и разбирает только общедоступные
title/meta descriptionвосьми официальных страниц Хабр Карьеры. Совпадение даётverified, транспортная ошибка — отдельныйunavailable, а изменившееся число останавливает op до ручной проверки; скрытые входом данные и автоматическая перезапись запрещены. - Результат аудита источника должен переживать процесс Dagster. Каждый включённый запуск записывает безопасный итог и публичные детали проверки в существующий PostgreSQL
audit_logsдо обработкиchanged;/statusраскрывает только время и агрегированные счётчики. Это делает последнюю фактическую проверку видимой после рестарта без новой таблицы, без доверия браузеру и без автоматического изменения зарплатного снимка. - Зарплатный справочный слой публикуется отдельно от vacancy datasets.
/salary-benchmarks, JSON и CSV экспортируют 50 профессий с direct/related/category coverage, географией, метрикой, периодом,n, tax status и первичным URL. Они сохраняютcurrent_market_claim=false, не содержат Premium-полей, не участвуют в score и не входят в Data Package официальных данных «Работы России», чтобы его федеральная лицензия ошибочно не распространялась на сторонние исследования; условия дальнейшего использования остаются source-specific. - Пустые ячейки не заполняются переименованными данными. Для каждой профессии точное число публикаций сохраняется отдельно от суммы направления; оба ряда показываются по неделям. Для зарплат официальный midpoint публикуется при
n≥3; выбор единого комплекта уровней уточнён решением 122. Только отсутствие подходящего уровневого источника закрывает явно подписанная общерыночная медиана опроса Профсоюза работников ИТ (n=1 539). Так UI получает 150 из 150 Junior/Middle/Senior карточек без выдуманных ролевых значений, скрытых источников и подмены нулей. - На странице профессии существует один комплект Junior/Middle/Senior. Комплект выбирается по качеству provenance и согласованности уровней согласно решению 122. Наибольшая сумма сама по себе не выигрывает. Повторные карточки official/prepared слоёв удалены;
n, период, scope и tax status остаются видимыми, а динамические графики сохраняют свои независимые слои. - Category-only покрытие снимается только честным смежным источником. Публичные SEO-медианы калькулятора Хабр Карьеры дают ML-разработчика 230 083 ₽ и ERP-программиста 147 500 ₽ на 2026-07-22. Они закрывают контекст NLP и SAP как
related_role, а неexact_role;nи gross/net остаются неизвестными. Покрытие становится 37 direct / 13 related / 0 category-only, при этом категорийные региональные значения продолжают существовать только как подписанный контекст. - Стоимость личного ведения согласуется по заявке. Вместо неподтверждённого фиксированного тарифа страница предлагает кандидату назвать комфортную стоимость в отдельном поле. Формат, объём работы и итоговые условия рассматриваются индивидуально; это не включает оплату и не обходит отдельные legal/payment guards.
- Предлагаемая стоимость хранится как часть обращения, а не заказа. Форма получает целое число рублей в диапазоне 1 000–1 000 000, сохраняет его отдельно и включает в письмо владельцу. Это значение не создаёт платёж, не меняет серверный каталог товаров и не может стать суммой списания без отдельного согласования и серверного заказа.
- Готовность платежей проверяется без выдачи credentials. Admin-only endpoint и карточка
/adminпоказывают отдельные checklist test/live и вычисленный ResultURL, но сериализуют только булевы признаки. Live-конфигурация дополнительно требует явныйPAYMENTS_STABLE_HTTPS_CONFIRMED=true; его можно выставить только после проверки постоянного HTTPS-домена и круглосуточного host, поэтому зависимый от ПК Tailscale Funnel не снимает блокировку. - SEO-метаданные категорий не зависят от повторного API-вызова. Восемь стабильных названий и описаний каталога продублированы в маленьком frontend reference-модуле.
generateMetadataчитает их локально, а SSR использует как fallback при временной недоступности backend; это исключает пустой description и повторяющийся generic title, не подменяя данные профессий. - Production standalone получает внутренний API-адрес при сборке и запуске. Next.js фиксирует destination для
/api/*во время build, поэтому одного runtime environment недостаточно. Production Compose передаётhttp://backend:8000в оба этапа, а fail-closed validator проверяет их совпадение; публичный Caddy остаётся единственной точкой входа.
techrole.ru is the canonical public host on the existing Selectel VDS. DNS stays with Selectel (A @ to 94.102.88.123, CNAME www to the apex) and Caddy terminates HTTPS. The legacy sslip.io URL is retained only as a redirect for continuity. Live payments remain disabled until DNS/TLS and Robokassa production prerequisites are independently verified.
The catalog keeps server-side filtering as the source of truth while adding native Russian/English suggestions and a combined category selector. Profession pages expose section anchors, compact non-duplicating facts and confidence labels derived from the underlying layer; temporary API failures have a bounded timeout and friendly recovery links. The only Premium product remains a one-time 30-day access package priced at 290 RUB in the server catalog; payment mode stays disabled until all live guards pass.
Seller identity is server-only configuration and is never committed. Legal pages render complete operational text from those variables without public owner-only review notes; the remaining activation checklist lives in the protected admin/payment documentation. Every profession gets a generated OG card and share/citation actions; comparisons use allowlisted canonical query links. The weekly report and RSS are public. IndexNow runs only after a successful materialization and fails the Dagster step on a rejected submission; the weekly Telegram job is opt-in and skips without credentials. None of these paths exposes Premium fields or enables real charges.
-
Три полные вилки достаточны для публикации зарплатного среза. Значение
MIN_SALARY_SAMPLEпо умолчанию снижено до3;n=3в интерфейсе называется базовой выборкой и не обозначается ненадёжным. Комплект Junior/Middle/Senior берётся из официального слоя только когда все три медианы присутствуют и не убывают с ростом уровня. При пропуске или инверсии все три видимых значения берутся из одного наиболее точного открытого исследования, а официальныйnостаётся рядом для проверки. Это не меняет первичные данные и не повышает зарплату искусственно. -
Premium-статус показывается без изменения существующих кнопок шапки. Отдельный клиентский индикатор появляется только после подтверждённого
/auth/me, работает в обеих темах и не делает публичные страницы зависимыми от серверных cookies. Личный кабинет и тарифы явно показывают уже активный Premium; правила уведомлений можно поставить на паузу, возобновить и удалить через owner-scoped API с CSRF. -
В публичном интерфейсе нет редакторских и инфраструктурных пометок для владельца. Удалены объяснение внутренней коррекции перевёрнутых зарплатных грейдов, pre-live предупреждения над юридическими документами, ссылка на администрирование в общем footer и подробности о контейнерах, Redis, Dagster и runtime-флагах на публичной странице статуса. Пользователь видит итоговые значения, источники, необходимые методологические ограничения и понятный статус свежести данных; технические checklist остаются только в защищённой админ-панели и документации.
-
Robokassa с фискальной номенклатурой открывается только через
POST. Сервер по-прежнему вычисляет продукт, сумму,Receipt,InvId, Return URL и подпись; браузер лишь превращает подписанный набор параметров в скрытую HTML-форму кhttps://auth.robokassa.ru. CSP разрешаетform-actionтолько собственному origin и этому официальному host. Для возвратного чека используется исходное наименование купленной услуги, а не административная причина возврата. Активация магазина сама по себе не снимает live guard: отдельно нужны credentials и подтверждённый статус «Робочеки СМЗ». -
Канонический endpoint формы Robokassa —
https://auth.robokassa.ru/Merchant/Index.aspx. Первый официальный test checkout обнаружил404на альтернативном URL из quick-start. Адрес заменён во всех runtime defaults, production allowlist и frontend contract tests на URL из актуальной документации интерфейса оплаты; выборPOSTдля запроса сReceiptсохранён. -
Сравнение не заканчивается пустой ячейкой и не выдаёт смешанное направление за зарплату роли. Для Junior/Middle/Senior сначала используется полный непротиворечивый 180-дневный срез профессии, затем подписанный уровневый ориентир открытого исследования и только при его отсутствии — непротиворечивый срез направления. Такой порядок не позволяет узкой профессии наследовать медиану направления, фактически сформированную другой массовой ролью: например, срез Data & AI в «Работе России» преимущественно состоит из администраторов баз данных и не является зарплатой Analytics Engineer. Публикации и удалённая работа могут использовать exact → category, но рядом всегда показываются источник и фактические числитель/знаменатель.
-
Выбор направления в каталоге применяет фильтр сразу. Категория остаётся серверным параметром и дополнительно проверяется перед SSR-рендерингом карточек. Изменение select отправляет GET-форму без отдельного клика; кнопка поиска расположена перед выбором направления. Premium-карточка сохраняет серверную цену 290 ₽, показывает прежний ориентир 1 349 ₽ зачёркнутым и визуально приглушается для уже активного доступа.
-
Форматы цитирования разборов имеют отдельные стабильные файловые URL. CSL-JSON, BibTeX и RIS публикуются через
/insight-citations/<slug>.*; прежние вложенные URL перенаправляются постоянно. Это обходит неоднозначность вложенного route handler в Next dev/standalone и сохраняет уже опубликованные ссылки. -
Зарплатный график показывает накопительную 180-дневную медиану и честный fallback направления. Решение 43 о скользящем 30-дневном окне заменено: оно оставляло пустой график даже при достаточной общей выборке, если три полные вилки не попадали в одно короткое окно, а последний день периода иногда вообще не становился точкой. Теперь недельная точка включает все совместимые RUB-вилки уровня от начала периода до своей даты, конец периода добавляется обязательно, а при слишком редком точном ряде целиком выбирается более наполненный ряд направления с явной подписью scope. Синтетические значения не создаются, порог
n=3сохраняется. -
В шапке существует одна Premium-кнопка с двумя состояниями. Решение 123 об отдельном индикаторе заменено: гостю та же кнопка ведёт к тарифу и показывает
Premium, а после подтверждения/auth/meона меняет состояние наPremium активени ведёт в кабинет. Дублирующий пункт навигации удалён; кнопка остаётся читаемой в светлой и тёмной темах. -
Публичные machine endpoints fail closed при недоступном источнике.
open-data, answer-first, AI index, research, full LLM context и RSS больше не возвращают правдоподобный пустой200. Они отвечают503/no-store/Retry-After, а успешный контент получает SHA-256 ETag, Last-Modified при наличии даты и304. Статические форматы связываются явными suffix URL иLink/alternate; зависимость от неявногоAcceptне вводится. -
Answer-first слой содержит только воспроизводимые публичные срезы.
/answersи/answers.jsonпоказывают top публикаций, salary by level с фактическимn, регионы, соседние недельные окна и ограничения со стабильными fragment URL. JSON-LD совпадает с видимым текстом,current_market_claim=false; Premium и подготовленные значения не попадают в слой. -
Каталог платежей является server-side registry с immutable order snapshot. Каждый SKU задаёт server price, результат/срок услуги, fulfillment, entitlement, receipt nomenclature и refund policy. Browser передаёт только allowlisted product code. Исторический заказ и возврат используют сохранённый snapshot даже после изменения текущей цены/каталога.
-
Test и live Robokassa credentials не взаимозаменяемы. Test использует отдельные Password №1/№2 с временным legacy fallback только для старых sandbox environments; live принимает только explicit live Password №1/№2/№3. Для НПД live дополнительно требует owner-only
PAYMENTS_ROBOCHEKI_SMZ_CONFIRMED=true; без него backend не стартует в live. -
Возврат продления уменьшает цепочку доступа на срок возвращённого SKU. Новые entitlement продления начинаются после текущего конца, account показывает полный effective expiry. Полный refund отзывает entitlement заказа и сдвигает последующие payment-entitlements, поэтому возврат ранней покупки не оставляет бесплатные 30 дней.
-
Аналитика является consent-based, first-party и честно ограниченной. Встроенный admin dashboard хранит HMAC browser ID, публичный path/target и hostname referrer без IP, email, raw User-Agent, query/forms/payment data; admin и automation исключаются. Crawler request считается только по заявленному официальному User-Agent и не доказывает личность. Фактическое число внешних AI-цитат недоступно; показываются только copy actions, AI referrals и declared crawler requests.
-
Live сохраняется только после расчётного control payment. 24 июля 2026 года readiness и официальный sandbox были зелёными, но InvoiceID 30–32 из-за недостаточного остатка остались в Robokassa
State=10; деньги, ResultURL и entitlement отсутствовали, а локальные заказы не получили выдуманный terminal status. После пополнения InvoiceID 33 на 290 ₽ получил расчётное состояние, обработанный подписанный ResultURL и отдельный Premium entitlement ровно на 30 дней. Фискальный чек подтверждён владельцем одновременно в Robokassa и «Мой налог», поэтому production разрешено оставить вrobokassa/live. -
Плотность публикаций, полнота зарплат и исследовательский ориентир показываются раздельно. Число публикаций не считается числом одновременно активных вакансий и не обещает большую зарплатную выборку. API и страница отдельно показывают дневные/недельные полные RUB-вилки и их долю. На странице остаётся один официальный график зарплаты: сплошные линии — накопительные наблюдения профессии или явно подписанного направления по опубликованному правилу отбора, пунктир — статичный ориентир одного непротиворечивого исследования для отсутствующего уровня. Карьерные карточки используют полный неубывающий комплект одного источника. Дублирующий график зарплаты подготовленной витрины удалён, а её
vacancy_countпереименован в расчётный объём и не смешивается с официальным слоем. -
Карточка профессии следует пользовательскому сценарию, а не внутренней модели данных. После hero первым содержательным блоком идёт стек, затем доходы, динамика, подготовленный расчётный ряд, компактный период, provenance и индекс. Прежняя верхняя сводка из четырёх карточек удалена; таблицы индекса/навыков/регионов и блок смежных ролей сохранены.
-
Редкая профессия получает подписанный контекст, а не пустой или смешанный график. Поток публикаций и полнота используют точную профессию при
total_publications >= 20, иначе одно явно названное направление. Зарплата сохраняет per-level exact-firstn>=3; инверсии не переставляются, а непротиворечивые карьерные карточки остаются отдельным представлением. -
Росстат 57-Т — широкий официальный фон
occupation_group. Средняя начисленная gross-зарплата группы ОКЗ не считается медианой, ролевым значением или размером выборки. Selecty, Antal и SuperJob не импортируются без ясного права повторной коммерческой публикации. -
Индекс
v1.1.0использует публичный salary input и больший вес качества. Веса: demand 28%, salary 24%, growth 16%, junior 12%, remote 10%, quality 10%. Остальные компоненты остаются prepared; старые строки версионируются, а API присоединяет только активную scoring version. -
Динамика зарплаты использует явную reference-фильтрацию, не меняя исходный слой. Базовая национальная медиана выбирается из уже опубликованных ориентиров в порядке
exact_role → technology → related_role → category. В динамический midpoint-ряд входят вилки не ниже 40% базы для Junior, 70% для Middle и 100% для Senior; после этого применяется fallbackprofession → category → статичный market reference. База, scope, коэффициенты и денежные пороги входят в публичный API и методологию. Счётчики публикаций, полнота, raw observations, salary benchmark cards и scoring не фильтруются и не пересчитываются этим правилом. -
Ручной Premium — отдельный от платежа административный override. Переключатель
/adminсоздаёт бессрочный entitlementadmin_override; режим Free отзывает толькоadmin_overrideи прежниеadmin_grant. Entitlement с источникомpayment:*не затрагивается: оплаченный доступ прекращается только штатным refund-процессом. Mutation требует admin RBAC + CSRF и фиксируется в audit log; список пользователей никогда не сериализует password hash, provider IDs или credentials. -
Главный зарплатный график показывает дневное скользящее среднее, а не накопительную медиану. Решение 130 заменено для этого представления: накопление с начала периода превращало линии в почти неизменные ступени и скрывало движение новых наблюдений. Каждая точка теперь является средним midpoint прошедших reference-фильтр полных RUB-вилок за предшествующие 30 дней при
n≥3. Динамический scope выбирается только при наблюдениях минимум с трёх разных дат в порядкеprofession → category → market; scope всегда подписан. Пороги 40%/70%/100%, сами базовые медианы, raw observations, полнота и карточки уровней не меняются. Статичный пунктир исследования остаётся только последним fallback без синтетической истории. -
Верхний зарплатный ориентир — одно значение и одна сравнительная шкала. Для каждой профессии выбирается ровно одна национальная медиана без грейда в порядке
technology → exact_role → related_role → category → market_level; fallback остаётся явно подписанным своим scope. Справа круговая диаграмма сравнивает её с максимальной медианой среди этих же 50 выбранных ориентиров, поэтому знаменатель воспроизводим и обновляется вместе с серверным каталогом. Источник, период и ограничения сохраняются ниже в provenance-карточках, но не дублируются в главной карточке. Логика и значения Junior/Middle/Senior не меняются; удалён только длинный поясняющий абзац перед ними. -
ФИО исполнителя публикуется только в оферте. Server-side реквизит остаётся обязательным и отображается в разделе реквизитов публичной оферты. Политика обработки данных и тексты согласий используют обозначение сервиса TechRole Index и дают ссылку на оферту, не дублируя ФИО на других страницах.
-
График полноты зарплатных вилок удалён из всех карточек профессий. Машинные данные и проверяемые счётчики полных вилок сохраняются в открытом наборе, но отдельный смешанный график публикаций, вилок и процентов больше не занимает место в пользовательском сценарии. Мобильная карточка использует однострочное прокручиваемое оглавление, компактные панели и KPI без горизонтального переполнения.
-
Недельные исследования — неизменяемые редакционные снимки открытого daily-набора. Раздел
/insightsобъединяет датированные исследования и методические разборы, а видимая ссылка «Исследования» добавлена в основную навигацию. Каждый недельный материал фиксирует два полных семидневных UTC-окна,dataModifiedAt, арифметику и оговоркуcurrent_market_claim=false; динамическая перезапись исторической статьи новыми данными запрещена. Исследования получаютReportJSON-LD и отдельные CSL-JSON/BibTeX/RIS, входят в RSS, sitemap, AI/LLM indexes и ссылаются на исходный набор вместо публикации непроверяемых выводов. -
Одобренный HH API остаётся отдельным ограниченным снимком. Приложение TechRole Index №26065 одобрено для заявленной публичной коммерческой агрегированной аналитики. Application token хранится только в окружении. Поиск выполняется по роли и алиасам в документированной глубине до 2 000 результатов на запрос, затем глобально дедуплицируется по vacancy id; обход лимитов, scraping и прокси запрещены. Raw payload ограничен allowlist без описаний, работодателей и контактов. Gross, net и неизвестный basis не смешиваются, а публично показываются только source-isolated агрегаты с явной оговоркой, что это не полная историческая база.
-
Истёкший application token HH обновляется только официальным
client_credentialsflow. Основной token можно задать напрямую; серверная пара client id/secret является необязательным production fallback. Провайдер обновляет token только при отсутствии token или документированной OAuth-ошибке истечения, повторяет запрос один раз и не публикует credentials, token response или секреты в API и логах. Это обеспечивает ночное обновление без ручного вмешательства и не меняет лимиты поиска. -
Подробные карточки HH обогащают только используемый публичный ролевой слой и публикуются агрегированно. После поиска возобновляемый detail-проход запрашивает
GET /vacancies/{id}только для вакансий последнего успешного query-match run, соблюдает задержку,Retry-Afterи пакетные commit. Allowlist включает ключевые навыки, языки, опыт, занятость, рабочие форматы/графики, профессиональные роли и публичные id/названия работодателей; описания, контакты, адреса, response URL и персональные данные исключены. Решение 151 уточнено: работодатели допустимы только как публичный топ-5 по профессии, весь оставшийся хвост объединяется в «Другие компании». Полные карточки и списки всех работодателей не публикуются. -
Индекс
v1.2.0использует собственный HH-срез каждой профессии. Веса остаются сопоставимыми сv1.1.0, но demand/growth/junior/remote/quality больше не берутся из prepared-витрины. Salary использует только exact-profession полные RUB gross-вилки приn≥5; меньшая выборка получает нейтральную peer-медиану и низкий quality, а не категорийное значение. HH-график публикаций переключается на точную профессию приn≥5, зарплатная история HH не заимствует направление. Все 50 строк пересчитываются в новой version, старая история не переписывается. -
Каталог сохраняет 50 ролей, но заменяет десять с
n<5более наблюдаемыми профессиями. Cloud, Analytics Engineer, PostgreSQL DBA, Firmware, Unreal, SOC, NLP, Ruby, generic JavaScript/TypeScript и SAP не теряются: их алиасы переходят к DevOps, Data Engineer, DBA, Embedded, Game Developer, информационной безопасности, AI Engineer, Backend, Frontend и ERP. Освободившиеся позиции занимают Technical Support Specialist, Product Manager, IT Project Manager, 1C Analyst, QA Engineer, System Engineer, UX/UI Designer, AI Engineer, ERP Specialist и Technical Writer. Старые публичные URL получают постоянные семантические redirect, а не ведут на случайную новую роль из той же строки миграции. -
Порог пригодности профессии для отдельной карточки — пять собственных HH-вакансий. На контрольном снимке
rules-v3классифицирует 11 132 из 42 153 уникальных вакансий, и минимальная выборка среди 50 ролей равна шести. Новые высокосигнальные роли дают от 59 до 523 точных совпадений. Длиннейший алиас имеет приоритет, явные отрицательные шаблоны отсеивают не-IT product/system/QA/technical-writer вакансии, а неоднозначность по-прежнему означает abstention. -
Расчётный ряд становится точной rolling-моделью HH, а не наследием демонстрационной витрины. Для каждой профессии и уровня сохраняется ежедневный объём её собственных публикаций в предшествующем 30-дневном окне; явный уровень заголовка приоритетнее документированного кода опыта HH. Nightly пересобирает этот слой после materialization, затем
v1.2.0повторно рассчитывает все 50 оценок. Данные направления не заполняют отсутствующую профессию. -
Карточка профессии показывает пользовательские ответы и не визуализирует внутреннюю схему данных. Скрыты provenance-блок «Слои, которые нельзя смешивать», служебная финальная строка, смежные роли, форма занятости, рабочие дни/часы, профессиональные роли, ГПД и инклюзивность. Навыки, полезные условия и регионы объединены в один рыночный блок. Удалены отдельные графики HH-зарплаты и интенсивности; основной salary-график доступен всем максимум за 30 дней, его 90/180 кнопки заблокированы без Premium, а другие временные графики доступны только Premium и занимают половину desktop-ширины. API обрезает сами временные массивы, поэтому ограничение не является только CSS. Employer dashboard сохраняет топ-5 плюс «Другие компании», но не кладёт текст поверх кольца: все подписи и числа вынесены в отдельный список.
-
Поисковое совпадение HH и каноническая классификация — разные отношения. Для каждой профессии и алиаса HH ищется документированной точной фразой только в поле названия вакансии (
search_field=name). Все выданные vacancy id сохраняются в отдельном many-to-many слое последнего полностью успешного запуска и дедуплицируются внутри профессии; одна вакансия может честно относиться к нескольким поисковым ролям. Публичные HH-счётчики, работодатели, навыки, rolling-метрики и score используют этот слой, а консервативныйVacancy.profession_idостаётся независимым и не переписывается результатом поискового запроса. Материализация выполняет полную замену снимка: связи и дневные срезы, исчезнувшие из нового успешного запуска, удаляются, поэтому устаревшие совпадения не накапливаются. -
Карточки Junior/Middle/Senior повторяют актуальные KPI главного зарплатного графика. Для каждого доступного уровня берутся та же последняя ненулевая точка скользящего 30-дневного среднего, тот же
nи тот же scope; сумма показывается в тех же округлённых тысячах рублей. Исследовательский ориентир используется только там, где у главного графика нет устойчивой точки. Отдельная строка «Вилки „Работы России“» удалена, потому что она создавала второе конкурирующее представление той же выборки. Допустимый scope после решения 161 — толькоprofession. -
Главная зарплата карточки профессии переходит на точные gross-вилки HH. И график, и Junior/Middle/Senior используют только query-match самой профессии, полную вилку
from+to,currency=RUR,gross=true, rolling window 30 дней иn≥5на точку; category/market fallback запрещён. Открытое исследование остаётся только явно подписанным статичным резервом отсутствующего уровня. Бесплатный API физически ограничивает массив 30 днями, Premium — 180 уже накопленными днями; UI не синтезирует прошлое до начала HH-наблюдений. «Работа России» остаётся отдельным provenance-слоем публикаций и больше не конкурирует с HH в основной зарплатной визуализации. -
Главная метафора пути реализуется кодом, а не тяжёлым циклическим видео. Детерминированные Canvas-частицы собираются в человеческий силуэт и реагируют на указатель, SVG-лента Мёбиуса связывает Junior/Middle/Senior с пульсирующим офером. DPR и число частиц ограничены, mobile получает более лёгкую вертикальную композицию,
prefers-reduced-motion— статичный кадр. Цвета вычисляются из текущей светлой/тёмной темы; переключение темы не требует отдельного медиафайла и не создаёт чёрной или белой подложки. -
Новый query-match снимок HH переключается только после полностью успешного поиска. Во время 270 запросов пары vacancy/profession и их алиасы накапливаются в памяти процесса, а прежний
last_run_idостаётся доступным публичному API. Массовое обновление связей выполняется одной финальной транзакцией только при нуле query/AI errors; partial run не делает карточки пустыми и не публикует смесь старых и ещё не законченных профессий. Устаревшие связи остаются с прежним run id и не входят в новый снимок. -
180-дневная Premium-история HH накапливается, а не выдумывается из текущей выдачи. Ежедневная materialization пересчитывает доступное поисковое окно, сохраняет уже закрытые более ранние дни и удаляет только записи старше 180 дней. Salary midpoint проходит тот же ролевой нижний порог, полную gross RUB-вилку и
n≥5; публичный главный график читает максимум последние 30 дней накопленного точного ряда, Premium — максимум 180. Если интеграция работает меньше 180 дней, API возвращает только фактически накопленный период. -
Ночной HH-конвейер завершает публикацию пересчётом всех оценок. После успешных поиска, detail-enrichment, materialization и построения 180-дневных окон Dagster пересчитывает
v1.2.0для всех 50 профессий. Неуспешный или частичный этап не запускает публикацию новых score, поэтому карточки не смешивают новый спрос со старой оценкой. -
Машинный каталог не строит невидимую историю зарплат повторно.
/open-data/publicationsзапрашивает только поля своего компактного контракта, а тяжёлые salary-history ряды остаются detail endpoint. Валидированный JSON каталога кешируется в Redis с календарной датой и порогом выборки в ключе: холодный расчёт остаётся воспроизводимым, повторные AI/health запросы не сканируют весь HH snapshot. -
Три карточки грейдов выбирают одну согласованную дату главного HH-графика. Решение 160 уточнено: берётся самая свежая дата, где у всех Junior/Middle/Senior есть собственная точка профессии с
n≥5, Middle не ниже Junior × 1,4, а Senior не ниже Middle × 1,3. Последние точки разных дат не смешиваются; category/market scope не допускается. Если такого среза ещё нет, исходный график остаётся без коррекции, а карточки честно используют подписанный комплект открытого исследования — значения HH не переставляются и не дорисовываются. -
Главный карьерный образ использует отдельные адаптивные художественные кадры и настоящий интерфейс поверх. Решение 162 заменено по выбранному визуальному направлению: световой Möbius-маршрут из листов данных имеет широкую и вертикальную композиции, а заголовок, поиск, источник и статус офера остаются доступным HTML. На мобильном загружается отдельный портретный кадр, обе темы используют собственные CSS-свет/затемнение, движение ограничено лёгким transform, а
prefers-reduced-motionостанавливает его. Сгенерированное изображение не содержит текста, логотипов или UI и не является источником данных. -
Зарплатные Junior/Middle/Senior — исчерпывающие ранговые сегменты всей совместимой HH-выборки. Решения 161 и 167 заменены: полные и односторонние вилки поддерживаемых валют после перевода по сохранённому курсу ЦБ, а gross и математически нормализованный net участвуют в одной сопоставимой gross RUB-модели. Размеры трёх сегментов выводятся из собственных требований роли к опыту со слабым prior 15/45/40 и минимумом пять записей при общем
n≥15; отсортированные значения делятся без пропусков, поэтому сумма трёхnравна полной выборке и шкала монотонна. Это расчётные рыночные сегменты, а не заявленные работодателями грейды; unknown tax basis исключается. -
Десять оставшихся малоданных карточек освобождают места массовым IT/digital-профессиям. Android, iOS, Flutter и React Native объединяются в Mobile Developer; SDET — в QA Automation, SRE и Platform Engineer — в DevOps, MLOps — в Machine Learning Engineer, Penetration Tester — в Information Security, Unity — в Game Developer. Освободившиеся позиции занимают администратор информационных систем, менеджер IT-продаж, специалист по автоматизации, специалист по внедрению, веб-дизайнер, SEO-специалист, интернет-маркетолог, оператор баз данных и менеджер по развитию. Старые URL перенаправляются на семантически ближайшую сохранённую роль.
-
Статичный художественный кадр не является главным карьерным эффектом. Решение 168 заменено реализацией из решения 162: Canvas в реальном времени собирает человеческий силуэт из сотен частиц, реагирует на указатель, а SVG/CSS ведут поток данных через три уровня к пульсирующему оферу. Композиция, плотность частиц и DPR адаптированы под mobile и обе темы; offscreen-анимация останавливается, reduced-motion получает готовый статичный кадр без загрузки растрового фона.
-
Публичная динамика использует ограниченную нормализованную шкалу. Для спроса и зарплаты по-прежнему сравниваются средние двух соседних окон, но разность делится на большее из двух средних, а итог ограничивается диапазоном от −100% до +100%. Это устраняет значения в тысячи процентов при маленьком предыдущем окне, сохраняет направление изменения и единый порог ±3% во всех карточках, рейтингах, ответах и расчётах индекса. Кэш карточки профессии получает новую версию схемы, поэтому старые проценты не переживают релиз.
-
Планета и зарплатные уровни используют разные, явно проверяемые desktop/mobile-композиции. На экранах от 768 px три флага закреплены на соседних плитах на один шаг левее, а мобильные якоря не меняются. На телефоне Junior, Middle и Senior одновременно помещаются в компактную трёхколоночную строку вместо скрытого горизонтального продолжения. WebGL-сцена начинает загружаться в первом кадре после гидратации без искусственной паузы, а плитки получают тематическое emissive-свечение без снижения геометрической детализации, DPR или качества теней.
-
Слово «Офер» является гравировкой ядра, а не отдельной золотой надписью. В обеих темах текст использует тёмную бордово-обсидиановую заливку, вдавленный контур, противоположные тень и блик и лёгкий наклон по поверхности; отдельный фон, рамка и внешний золотой glow отсутствуют. HTML-слой сохраняет читаемость и доступность, а WebGL-геометрия, DPR и скорость загрузки не усложняются.
-
Вся карточка популярной профессии является одной ссылкой. Заголовок больше не создаёт растянутую псевдоссылку поверх карточки: один доступный
Linkоборачивает карточку целиком, поэтому область стрелки, сенсорное нажатие и клавиатурный focus ведут на одну и ту же профессию без зависимости от hover-анимации.