diff --git a/memory/_append_per_flat.py b/memory/_append_per_flat.py new file mode 100644 index 00000000..4588b0f4 --- /dev/null +++ b/memory/_append_per_flat.py @@ -0,0 +1,71 @@ +"""Append v3 — per-flat endpoint discovery for ДОМ.РФ. +Visible script. Manually delete when done.""" +import json +import pathlib + +target = pathlib.Path(__file__).parent / "memory-gendesign.jsonl" + +addendum = { + "type": "entity", + "name": "DataSources_DomRF_PerFlat_Apr26", + "entityType": "research", + "observations": [ + "Дата: 26.04.2026 вечер. ОТКРЫТИЕ: ДОМ.РФ выдает per-квартира данные через GET /сервисы/api/kn/flats. До этого думали что только per-ЖК доступен", + "Endpoint: GET https://наш.дом.рф/сервисы/api/kn/flats?regions=66&offset=0&limit=N&flatStatus=free,booked", + "Auth: Authorization: Basic MTpqd2U= (тот же что для всех /сервисы/api/kn/*)", + "Параметры: regions (через запятую), offset, limit, flatStatus, + любые из 30+ фильтров (priceFrom/To, areaFrom/To, roomCount, type, energyEff, wall, hasParking, и т.д.)", + "По Свердл. обл. возвращает 24 441 квартиру в продаже/брони. Через offset/limit можно качать пачками (например limit=500, ~50 запросов на регион)", + "Структура per-flat (17 полей):", + "id (int) — глобальный ID квартиры в ЕИСЖС", + "odsId (string) — формат objId/sectionNum/flatNum (например '64057/2/55') — связь с реестром проектных деклараций", + "elemId (UUID) — токен для URL детальной страницы квартиры", + "type (string) — Квартира-студия / 1-комнатная / 2-комнатная / 3-комнатная / 4+комнатная / Апартаменты", + "number (string) — номер квартиры по застройщику", + "isStudio (bool)", + "totalArea (float) — общая площадь м² (это та что продаётся)", + "livingArea (float) — ЖИЛАЯ площадь м² (без коридоров/санузлов) — для финмодели важно отдельно", + "rooms (int) — количество комнат (для студии = 1)", + "status (enum) — free / booked / sold", + "price (int) — полная цена в ₽", + "pricePerSquareMeter (int) — цена м² в ₽", + "priceChangeDate (string DD-MM-YYYY HH:MM) — дата последнего изменения цены ⭐ КЛЮЧЕВОЕ ПОЛЕ для velocity", + "socialMortgage (bool) — доступна соц-ипотека", + "numberFloors (int) — этажность дома", + "floor (int) — этаж этой квартиры", + "image {attachmentId, attachmentIdSmall, hash, size, source, type} — type='layout' = планировка квартиры. URL для скачивания: /api/ext/file/{attachmentId}?type=png", + "objInfo {hobjId, objTransferPlanDt, objReady100PercDt, objCommercNm, objAddr} — связь с ЖК (плановая дата передачи + дата готовности + название + адрес)", + "🔥 priceChangeDate — это золотая жила. Снимая снапшот раз в день, мы видим:", + "1) Какие квартиры подешевели/подорожали (delta price)", + "2) Какие квартиры перешли в booked/sold (status changes)", + "3) Velocity по ЖК: кол-во квартир сменивших status за период", + "4) Эффективность ценовой стратегии: ЖК с активными priceChangeDate (последние 7 дней) vs ЖК где цены не двигались месяцами", + "Это ровно то что нужно для Plan A 'плитка 3 — за какое время продастся': вывожу absorption rate (квартир/мес) для ЖК-аналогов в радиусе 5 км → проектирую на наш концепт", + "🛠 ETL-стратегия для backend:", + "1) Daily full-refresh: для каждого региона из таргетов (66=Свердл, 50=МО, 77=Москва) → loop offset/limit с шагом 500 → upsert в таблицу dom_rf_flats", + "2) Daily delta tracking: SQL diff dom_rf_flats vs dom_rf_flats_yesterday → вычислить (a) кто перешёл в sold, (b) кому изменилась цена, (c) кто новый. Записать в dom_rf_flat_events", + "3) Materialized views: median(pricePerSquareMeter) по (region, type, year_quarter), absorption_rate по hobjId (sold quarters / 30 days)", + "📦 SQL schema для миграции (Stage 2a):", + "CREATE TABLE dom_rf_flats (id BIGINT PRIMARY KEY, ods_id VARCHAR(50), elem_id VARCHAR(64), obj_id INT, hobj_id INT, type VARCHAR(50), number VARCHAR(20), is_studio BOOL, rooms SMALLINT, total_area NUMERIC(8,2), living_area NUMERIC(8,2), price NUMERIC(12,2), price_per_sqm NUMERIC(10,2), price_change_dt TIMESTAMPTZ, status VARCHAR(20), social_mortgage BOOL, floor SMALLINT, num_floors SMALLINT, layout_attachment_id VARCHAR(80), obj_commerc_nm TEXT, obj_addr TEXT, obj_transfer_plan_dt TIMESTAMPTZ, obj_ready_dt TIMESTAMPTZ, last_synced_at TIMESTAMPTZ, raw JSONB, region_cd SMALLINT)", + "Индексы: (region_cd), (hobj_id), (status), (type), (price_per_sqm), (price_change_dt). Для радиус-запросов нужно JOIN с dom_rf_objects по hobj_id чтобы получить geom", + "CREATE TABLE dom_rf_flat_events (flat_id BIGINT, event_dt TIMESTAMPTZ, event_type VARCHAR(20), old_value JSONB, new_value JSONB, PRIMARY KEY(flat_id, event_dt)) -- тип event: 'status_change', 'price_change', 'new_listing', 'delisting'", + "💎 Что ещё можно подсосать через этот endpoint:", + "Скачивать ПЛАНИРОВКИ всех квартир (image.attachmentId, type='layout') — это набор ~24K image для Свердл. Можем использовать для AI-генерации (тренировка модели на схемах квартир) или показывать пользователю аналоги", + "objInfo.objTransferPlanDt vs objReady100PercDt — это плановая дата передачи квартир (когда фактически передают по ДДУ) vs дата готовности дома (когда строительство закончено). Лаг между ними обычно 3-6 мес и важен для absorption analysis", + "🔬 Качество данных: проверил на 2 квартирах из ЛО ('LIBERTY DOM'), цены 2.6М и 2.7М ₽, площадь 22 и 23 м² — реалистично. priceChangeDate '30-11-2025' = 5 мес. назад → значит эти квартиры в LIBERTY DOM плохо продаются (цена не двигалась)", + "Юридический статус (повтор): данные публикуются в ЕИСЖС в соответствии с 214-ФЗ как открытая информация по строящимся объектам. Использование в коммерческом продукте легально, для реестра ПО декларируем 'интеграция с ЕИСЖС ДОМ.РФ'", + ], +} + +relations = [ + {"type": "relation", "from": "DataSources_DomRF_PerFlat_Apr26", "to": "DataSources_DomRF_Catalog_Apr26", "relationType": "extends"}, + {"type": "relation", "from": "DataSources_DomRF_PerFlat_Apr26", "to": "PlanA_AnalyticsFirst_Apr25", "relationType": "fully_unblocks"}, + {"type": "relation", "from": "DataSources_DomRF_PerFlat_Apr26", "to": "SalesTracking_Strategy_Apr25", "relationType": "supersedes"}, +] + +with target.open("a", encoding="utf-8") as f: + f.write(json.dumps(addendum, ensure_ascii=False) + "\n") + for r in relations: + f.write(json.dumps(r, ensure_ascii=False) + "\n") + +print(f"Appended 1 entity + {len(relations)} relations") +print(f"Final line count: {sum(1 for _ in target.open('r', encoding='utf-8'))}") diff --git a/memory/memory-gendesign.jsonl b/memory/memory-gendesign.jsonl index 333497da..df874b91 100644 --- a/memory/memory-gendesign.jsonl +++ b/memory/memory-gendesign.jsonl @@ -17,6 +17,15 @@ {"type":"entity","name":"PlanA_AnalyticsFirst_Apr25","entityType":"decision_pending","observations":["Дата: 25 апреля 2026, вечер. Обсуждали с Антоном (второй разработчик)","Новые вводные: Антон сказал, что есть доступ ко всем сделкам для анализа. Детали по данным будут в понедельник 27 апреля 2026","Предложение Антона: сначала аналитика по продажам (что строить, какие квартиры, площади), потом всё остальное","Решение (предварительное, ждём детали): НЕ полный pivot. Перестановка порядка этапов: Stage 1 = Sales Analytics (нед 2-5), Stage 2 = Generative со Smart Defaults из аналитики (нед 6-10), Stage 3 = Site Finder с Velocity Score (нед 11-12)","Обоснование не полного pivot: Generative — главный дифференциатор против rTIM/TestFit. Без него конкурируем напрямую с ЕРЗ/ДОМ.РФ (у них 5 лет данных и команда). Аналитика становится мозгом для Generative и Site Finder, а не их заменой","Концепция «3 плитки»: ввод участок + класс жилья → (1) ЧТО СТРОИТЬ (unit-mix с velocity), (2) СКОЛЬКО ПРОСИТЬ (распределение цен м² по классам + тренд), (3) ЗА КАКОЕ ВРЕМЯ ПРОДАСТСЯ (absorption curves по АНАЛОГам). Кнопка «Сгенерировать концепцию» ведёт в Generative","Плюс нового плана: продаваемый артефакт уже после мес 1 (раньше был конец мес 2). Меньше технического риска. Другой ICP (инвестдепартамент вместо архитектуры)","КРИТИЧНО: «доступ ко всем сделкам» — это фундамент или мина. Бывают 4 сценария: 1) Легальный богатый ongoing (ЕРЗ Premium/банк/админ) → полный pivot, аналитика фундамент. 2) Легальный ограниченный → план A + шахматки параллельно для ongoing. 3) Серый канал → НЕ строим бизнес на этом, используем только «для себя». 4) Обещают но не дают → идём по плану шахматок, ждём","6 блоков вопросов к Антону на 27 апреля: Природа канала (что именно за доступ); Поля данных (цена/метраж/комнатность/покупатель?); Покрытие (регионы/история); Свежесть (разовая выгрузка или ongoing); Условия использования в коммерческом продукте; Цена доступа","Что делаем до пн (не останавливаемся, но с осторожностью): SSH-ключ, выбор хостинга (Beget/Timeweb), Stage 0 скелет уже сделан. НЕ начинаем Shapely геометрию или парсер ПКК — приоритет может сместиться","Шахматки застройщиков НЕ отменяются даже если данные Антона полные: 1) Сделки в Росреестре регистрируются с лагом 1-3 месяца. 2) Бронирования и доступность — сделки этого не показывают. 3) Маркетинговая цена vs фактическая различается","Сообщили Кене ответ Антону: направление в точку, но не полный pivot. Ждём детали по данным в пн. Вижу 6 блоков вопросов. От ответов зависит тип плана","Уточнение 25.04.2026 вечер: Антон подтвердил — речь идёт о ДДУ (Договорах Долевого Участия). Это идеальный источник для аналитики: ground truth по продажам новостроек, выше качеством чем шахматки","ДДУ поля стандартно: кадастровый номер квартиры, площадь, цена, дата регистрации, застройщик, ЖК. иногда комнатность/этаж/секция. ПН-поверхность: ПДн покупателя часто включены — обязательное обезличивание до записи в нашу БД (152-ФЗ)","Лаг ДДУ: 30-90 дней от реальной сделки до регистрации в Росреестре. Поэтому шахматки застройщиков НЕ отменяются — они дают 'today snapshot', ДДУ дают 'historical truth'","5 блоков к понедельнику (сужены до ДДУ-специфики): 1) Канал — держатель и договор (DOM.RF API/ЕРЗ Premium/банк/администрация/другое). 2) Поля (все ли обязательные + комнатность и ПДн). 3) Покрытие (регионы/история). 4) Свежесть (разовая/ongoing/real-time и лаг). 5) Условия коммерческого использования","Преимущество ДДУ над шахматками: ground truth по фактической цене (не маркетинговой), историческая глубина, юридически чистый источник (если канал легален), data moat относительно TestFit/rTIM"]} {"type":"entity","name":"Runbook_VPS_Setup_Beget","entityType":"runbook","observations":["Источник: фактическая настройка сервера Jolly Yakov в Beget Cloud, 25.04.2026. Сервер 2 vCPU / 4GB RAM / 40GB NVMe / Ubuntu 24.04, IP 46.173.16.127","ШАГ 1: Создание сервера в cp.beget.com/cloud → Создать сервис → VPS → Standard 3+ GHz → 2 ядра/4GB/40GB. ОС: Ubuntu 24.04. Включить публичный IPv4. После создания сервер получает IP, root пароль выслан на email","ШАГ 2: Генерация SSH-ключа локально (если ещё нет). Windows PowerShell: ssh-keygen -t ed25519 -C 'gendesign-deploy' -f $HOME\\.ssh\\gendesign_deploy. Пароль оставить пустым (Enter Enter) — нужно для автодеплоя","ШАГ 3: Добавление публичного ключа на сервер через панель Beget. При проблемах со SSH (Connection reset, fail2ban) — войти через VNC терминал в браузере и добавить в ~/.ssh/authorized_keys","ШАГ 4: Первый вход по SSH с явным ключом. Команда: ssh -i $HOME\\.ssh\\gendesign_deploy root@. При первом коннекте спросит fingerprint — yes","ШАГ 5: На сервере (root): обновить пакеты 'apt update && apt upgrade -y'. Если [-f /var/run/reboot-required] то ребут. Установить hostname/timezone: hostnamectl set-hostname gendesign; timedatectl set-timezone Europe/Moscow","ШАГ 6: Создание non-root пользователя. adduser gendesign (задать сильный пароль, остальное Enter). usermod -aG sudo gendesign. Копия SSH-ключа root→gendesign: mkdir -p /home/gendesign/.ssh && cp ~/.ssh/authorized_keys /home/gendesign/.ssh/ && chown -R gendesign:gendesign /home/gendesign/.ssh && chmod 700 /home/gendesign/.ssh && chmod 600 /home/gendesign/.ssh/authorized_keys","ШАГ 7: Тест первого входа как gendesign. Важно: хранить root-сессию открытой как safety net до конца. В новом окне: ssh -i $HOME\\.ssh\\gendesign_deploy gendesign@. Проверка sudo: sudo whoami → root","ШАГ 8 (Windows-специфика): SSH config не создавать через Notepad — он добавляет .txt и сохраняет в UTF-8 с BOM. Правильный способ: PowerShell heredoc | Out-File -Encoding ascii -FilePath $HOME\\.ssh\\config. Блок Host: HostName , User gendesign, Port 22, IdentityFile ~/.ssh/gendesign_deploy, IdentitiesOnly yes, ServerAliveInterval 60. Критично включать IdentitiesOnly yes — иначе Windows OpenSSH перебирает все ключи и получает MaxAuthTries reset не дойдя до нужного","ШАГ 9: Отключение root-логина и паролей в SSH. ТОЛЬКО ПОСЛЕ подтверждения gendesign работает и sudo работает. sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak; sudo nano /etc/ssh/sshd_config → PermitRootLogin no, PasswordAuthentication no, PubkeyAuthentication yes. sudo sshd -t для валидации. sudo systemctl reload ssh","ШАГ 10: Критический тест в третьем окне: ssh gendesign → пускает по ключу. В четвёртом: ssh root@ → должен отказать. Старую root-сессию закрываем только после этого","Распространённые грабли: 1) Connection reset при неверном пароле = fail2ban Beget. Обход через VNC. 2) Notepad вместо config пишет config.txt — проверять ls $HOME\\.ssh\\. 3) Windows OpenSSH без IdentitiesOnly перебирает все ключи. 4) Группа sudo не подхватывается без выхода-входа. 5) При UNPROTECTED PRIVATE KEY FILE использовать icacls /inheritance:r /grant:r '<имя>:(R)'","Статус 25.04.2026 вечер: Шаги 1-7+8 выполнены. gendesign@46.173.16.127 работает по ключу, sudo работает. Осталось: SSH hardening (Шаг 9), UFW firewall, установка Docker, клонирование репо в /opt/gendesign","Статус 25.04.2026 вечер (обновлён): Базовая настройка VPS завершена. gendesign@46.173.16.127 работает по ключу, sudo работает. UFW активен (22/80/443). SSH hardening: PermitRootLogin no, PasswordAuthentication no, PubkeyAuthentication yes через drop-in /etc/ssh/sshd_config.d/00-gendesign-hardening.conf (приоритет 00- перебивает cloud-init defaults). Docker и git установлены. Репо клонировано в /opt/gendesign через deploy key github_deploy. PostgreSQL+PostGIS и Redis работают в докер-компоуз. PostGIS_Version() = 3.4 USE_GEOS=1 USE_PROJ=1 USE_STATS=1","Осталось: GHCR setup (замена YCR на GHCR в deploy.yml + docker-compose.prod.yml), GitHub Secrets, Caddy reverse proxy, домен, первый автодеплой backend+frontend","ВЕХА 25.04.2026 вечер: Первый успешный автодеплой. GitHub Actions воркфлоу deploy.yml прошёл полный цикл: build обоих образов (backend ~2.5мин, frontend ~1мин) → push в GHCR (ghcr.io/lekss361/gendesign-backend, gendesign-frontend, приватные) → SSH на сервер → docker compose pull (с PAT-авторизацией) → up -d. /health отвечает {status:ok, environment:prod}","GHCR пакеты остаются ПРИВАТНЫМИ (решение пользователя для защиты алгоритмов). Сервер авторизуется в GHCR через PAT с read:packages, сохранён в ~/.docker/config.json. PAT с длительным сроком или без срока","Исправлены во время деплоя: backend Dockerfile (убрал невалидный conditional COPY для alembic, перешли на uv sync без --frozen до генерации lockfile); frontend package.json (react-leaflet 4→5 для совместимости с React 19); frontend Dockerfile (npm install --legacy-peer-deps вместо npm ci, пока нет lockfile); frontend/public/.gitkeep для Next.js standalone build","Статус инфраструктуры к 25.04.2026 19:00: VPS Beget работает (Москва, 2/4/40 NVMe). UFW + SSH hardening + non-root user. Docker compose prod поднят с 5 сервисами (postgres+redis+backend+frontend+caddy). GitHub Actions автодеплой работает на push в main. /health публичен внутри VM (127.0.0.1:8000)","Осталось до «infra-ready»: домен, Caddy TLS с Let's Encrypt на этот домен, регулярные pg_dump в Object Storage, frontend NEXT_PUBLIC_API_BASE_URL на реальный домен (сейчас пустая). И дальше — Stage 1 разработки по выбранному PlanA_AnalyticsFirst_Apr25 (после понедельника с Антоном по ДДУ-данным)"]} {"type":"entity","name":"Hardlink_Test_Apr25","entityType":"test","observations":["Тестовая запись, дата 25.04.2026 поздний вечер","Цель: проверить, что после пересоздания хард-линка записи MCP попадают в репо-файл memory/memory-gendesign.jsonl","Ожидаемый результат: размер репо-файла увеличится, git status покажет modified","Наблюдение: предыдущий хард-линк разорвался (LinkType был null, синхронизация репо-файла с MCP-папкой прекратилась). Репо-файл был 16550 байт, источник 43957 байт","Если тест пройдёт (синхронизация восстановлена) — рабочий цикл из README снова валиден. Если не пройдёт — нужен sync-memory.ps1 скрипт или альтернатива"]} +{"type":"entity","name":"Domain_Setup_Apr26","entityType":"infrastructure","observations":["Дата: 26.04.2026","Домен: gendsgn.ru, регистрация в Beget, срок до 26.04.2027","Стоимость: 199 ₽ первый год, 420 ₽/год продление. Сравнение из браузера 26.04.2026: Reg.ru 169₽ первый год + ~890₽ продление; Timeweb 200/399; nic.ru 747₽ регистрация (премиум)","Решение: Beget — потому что (1) хостинг VPS уже там, единая админка и единая платежка для бухгалтерии; (2) продление 420₽ — лучшее на рынке; (3) free DNS включён","DNS-серверы: ns1.beget.com, ns2.beget.com (плюс ns1.beget.pro, ns2.beget.pro как backup)","A-записи прописаны 26.04.2026: @ (apex) и www → 46.173.16.127. Beget NS отдают корректно (проверено nslookup gendsgn.ru ns1.beget.com → 46.173.16.127). Публичные резолверы (8.8.8.8, 1.1.1.1) на 26.04 ещё NXDOMAIN — пропагация .ru registry → public DNS, ожидаемо 30 мин - 4 часа","Caddyfile обновлён под gendsgn.ru: основной site block с auto-TLS Let's Encrypt, www → 301 redirect на apex, :80 fallback для IP-доступа сохранён. Закоммичен 68e95aa, запушен в main. На VM не применён (старый deploy.yml не делает git pull)","deploy.yml будет дополнен `git fetch + reset --hard origin/main` + `caddy reload --config /etc/caddy/Caddyfile` после `up -d`. Закоммитим и запушим когда DNS пропагируется. Это финальный шаг для HTTPS","Важное правило отрасли: с 1 сентября 2026 владелец .ru-домена обязан иметь подтверждённую учётку ЕСИА (Госуслуги) — иначе домен не продлят. Для ИП/ООО — через ЭДО/СБИС. Касается всех регистраторов одинаково. К моменту продления (26.04.2027) ООО уже должно быть зарегистрировано — учёт без проблем"]} +{"type":"entity","name":"Sentry_Setup_Apr26","entityType":"infrastructure","observations":["Дата: 26.04.2026","Sentry организация: qqq (qqq-5y.sentry.io), free tier (5К ошибок + 10К транзакций / мес)","Sentry проект: gendesign-backend, платформа FastAPI, регион Frankfurt (ingest.de.sentry.io)","DSN сохранён в /opt/gendesign/backend/.env как SENTRY_DSN. На локальной машине — не нужен (Sentry init в backend/app/main.py условный — отключён если DSN пустой)","Конфиг init: sentry_sdk.init(dsn=settings.sentry_dsn, environment=settings.environment, traces_sample_rate=0.1) — 10% запросов трейсятся для performance","Алерты: «high priority» по умолчанию + email на alexandro361@gmail.com","Тестовое событие отправлено и получено 26.04.2026 — event_id 67f05bdd59234a608a828fa81f72ded5. Sentry интеграция работает на проде","Важная операционная заметка: docker compose restart НЕ перечитывает env_file. После правки backend/.env нужно `docker compose -f docker-compose.prod.yml up -d --force-recreate --no-deps backend`. Применимо к любым env-изменениям, не только Sentry","DSN — не критичный секрет (в frontend SDK всегда зашит публично в JS-бандл). Держим в .env, не в репе. Ротация при необходимости через Sentry → Settings → Client Keys (DSN) → New Key"]} +{"type":"entity","name":"OpsBundle_Apr26","entityType":"infrastructure","observations":["Дата: 26.04.2026","Подготовлен пакет инфраструктурных правок (в working tree, не закоммичено): Alembic baseline + TIGER cleanup + pre-commit + pg_dump backups + Makefile targets","Alembic: создан backend/alembic.ini + backend/alembic/env.py (импорт Base из app.core.db, моделей через app.models.*) + script.py.mako + versions/.gitkeep. Первая реальная миграция — в Stage 2a, когда Parcel-модель будет финализирована. Сейчас versions/ намеренно пуст","TIGER cleanup: backend/db/init/99_drop_unused_extensions.sql дропает postgis_tiger_geocoder + postgis_topology + fuzzystrmatch на свежем postgres-volume (через bind-mount ./backend/db/init:/docker-entrypoint-initdb.d:ro в обоих compose-файлах). Срабатывает только при первой инициализации пустого volume","Pre-commit: .pre-commit-config.yaml в корне (trailing-whitespace, end-of-file-fixer, ruff lint+format на backend/, prettier на frontend/, detect-private-key, check-yaml/toml). Pre-commit добавлен в dev-deps backend/pyproject.toml. Локально — `python -m pre_commit install` или `make pre-commit-install`","pg_dump: ops/backup.sh — cron-скрипт с опциональной выгрузкой в S3. Ретенция 7 дней локально. ops/restore.sh с защитой RESTORE_CONFIRM=yes","Makefile: добавлены `make migration NAME=...` и `make pre-commit-install`. `make migrate` использует `alembic` напрямую (uv нет в production-образе)","backend/Dockerfile: COPY alembic.ini + COPY alembic в обе стадии (builder и runner)","S3 для бэкапов: пока не настроено. Когда настроим Selectel или Yandex Object Storage, env-конфиг — /etc/default/gendesign-backup на VM, cron — `30 3 * * * /usr/local/bin/gendesign-backup`"]} +{"type":"entity","name":"Production_Wipe_Apr26","entityType":"event","observations":["Дата: 26.04.2026","Прод полностью пересоздан с нуля для чистого состояния. Не было критичных данных — БД содержала только postgis-defaults","Команды на VM: `docker compose down -v` → `docker rmi` GHCR-образов и базовых (caddy:2, postgis/postgis:16-3.4, redis:7-alpine) → `git pull --ff-only` (Caddyfile, compose) → `docker system prune -af --volumes`","Re-deploy через GitHub Actions Deploy → все 5 контейнеров поднялись с image-тегом 08a63f6 (главный коммит на момент wipe)","Состояние после re-deploy: postgres healthy, redis healthy (новый healthcheck), backend healthy (фикс curl-healthcheck в multi-stage Dockerfile), frontend up, caddy up. PostGIS 3.4 USE_GEOS=1 USE_PROJ=1","TIGER-мусор вернулся (как ожидалось) при свежей инициализации postgis-image. Будет автоматически удаляться при следующих wipe-ах когда применим OpsBundle_Apr26 (init-скрипт 99_drop_unused_extensions.sql)"]} +{"type":"entity","name":"DockerOps_Apr26","entityType":"infrastructure","observations":["Дата: 26.04.2026","Серия Docker-фиксов задеплоена в main: e22aeb9 (multi-stage + npm ci + healthcheck + same-origin API), d0909e6 (ruff fixes), 08a63f6 (mypy fixes), 68e95aa (real domain Caddyfile)","backend/Dockerfile: переписан на multi-stage (builder с build-essential + libgdal-dev + uv sync; runner только с runtime-либами libpq5, libgdal36, libproj25, libgeos-c1t64, libcairo2, libpango*, curl). Финальный образ 1.44 GB вместо ~2 GB. Запускается под non-root пользователем `app` (uid 1000)","backend curl healthcheck в docker-compose.prod.yml теперь работает (до этого был баг — curl не был установлен в slim-образе)","frontend/Dockerfile: переключён на `npm ci --legacy-peer-deps` вместо `npm install`. Запускается под `node` (uid 1000). package-lock.json закоммичен","docker-compose.prod.yml + dev: убран dead NEXT_PUBLIC_API_BASE_URL (Next.js bakes NEXT_PUBLIC_* на build-time, runtime override no-op). Frontend ходит на same-origin /api/* — в проде через Caddy, в dev через Next.js rewrites (BACKEND_URL env)","Caddyfile: исправлен баг — handle_path /api/* стрипал префикс /api, FastAPI router на /api/v1/* возвращал 404. Заменено на handle /api/*","backend/uv.lock сгенерирован (через docker run python:3.12-slim + pip install uv && uv lock). Дальнейшие билды детерминированы: `uv sync --frozen --no-dev`","Ruff config: ignore=['RUF001','RUF002','RUF003'] — проект русскоязычный, кириллица в комментариях/docstring (МСК-66 etc.) — норма, иначе постоянно ругается"]} +{"type":"entity","name":"HTTPS_Live_Apr26","entityType":"milestone","observations":["Дата: 26.04.2026, ~14:41 MSK","Прод официально живой на HTTPS: https://gendsgn.ru/ возвращает 200, https://gendsgn.ru/health → {\"status\":\"ok\",\"environment\":\"prod\"}, https://www.gendsgn.ru/ → 301 на apex","Цепочка: ЕСИА-верификация в Beget → реестр .ru обновился → DNS пропагировался к 8.8.8.8/1.1.1.1 → push deploy.yml с git-pull+caddy-reload не делался (юзер сделал руками на VM) → пришлось вычищать caddy_data volume чтобы сбросить старый retry-state на gendesign.example.ru и Let's Encrypt staging fallback → Caddy перезагрузился чисто → cert получен с production Let's Encrypt","Урок: при wipe `down -v` мы убрали весь caddy volume включая ACME-state. Если бы оставили — Caddy продолжал бы фоновые retry-задачи на старый плейсхолдер gendesign.example.ru, и accumulated failures могли бы триггернуть staging fallback. На будущее: `caddy reload` не сбрасывает retry queue — для чистого старта нужен `restart` или volume-wipe","GitHub-issues #1 (ЕСИА) и #2 (push deploy.yml) можно закрывать как done","Стек живет: HTTP/2 + HTTP/3 (alt-svc), Caddy reverse-proxy фронт+бэк, FastAPI корректно отвечает через /api/* префикс (handle вместо handle_path сработал)"]} +{"type":"entity","name":"DataSources_Analysis_Apr26","entityType":"research","observations":["Дата: 26.04.2026. Глубокий анализ источников данных для GenDesign — проверено через debug-браузер на наш.дом.рф","🥇 ГЛАВНАЯ НАХОДКА: ДОМ.РФ имеет ОТКРЫТЫЕ JSON API без аутентификации. Это меняет всё для Plan A и Site Finder","ДОМ.РФ Аналитика API (`naash.dom.rf/portal-analytics/api/`): /dictionaries/regions, /dictionaries/developers, /dashboard/by-room-count, /dashboard/by-flat-area, /dashboard/by-developer, /dashboard/by-region. Возвращают JSON: по каждому региону id, flatCount, area, и details — разбивка по комнатности (ONE/TWO/THREE/FOUR с площадями и %). Базовый URL хоста: https://xn--80az8a.xn--d1aqf.xn--p1ai (он же наш.дом.рф). Без auth, accept: application/json. Свердл. обл. id=66, flatCount=115205, area=5.67M м²","ДОМ.РФ Каталог квартир API (`/сервисы/api/kn/`): /flats/map?latitudePoint1=...&latitudePoint2=...&longitudePoint1=...&longitudePoint2=...&flatStatus=free,booked возвращает per-ЖК записи {objId, latitude, longitude, priceMin, objStatus}. /flats/filters даёт диапазоны фильтров. Auth: header `Authorization: Basic MTpqd2U=` (= base64('1:qwe')) — хардкод в фронте, действующий ключ. По Свердл. обл. видно 24 441 квартир в продаже","ДОМ.РФ доступные на per-flat фильтры (которые присутствуют в JSON): кол-во комнат (студия/1/2/3/4+), год сдачи (Сдан/2026/2027/2028+), цена (от 2.6М до 4.8 млрд), площадь (16-1001 м²), этаж (1-68), цена м² (82К-7.2М), отделка (без/черновая/предчистовая/чистовая/под ключ), класс (типовой/комфорт/бизнес/элитный), материал стен (блоки/кирпич/панель/монолит/монолит-кирпич), тип (жилое/нежилое), эскроу, безбарьерная среда, благоустройство, энергоэффективность, наличие апартаментов","ДОМ.РФ покрытие России: 2.4 млн квартир в строящихся домах, 118.7 млн м². По регионам ТОП-20 включает Свердл.обл (4-е место, 5.67М м²). По девелоперам ТОП-15: Самолет, ПИК, DOGMA, ТОЧНО, ФСК, Брусника, Setl Group, ЛСР и др.","ДОМ.РФ распределение площадей квартир в РФ: 0-25м²: 6%, 25-35м²: 17%, 35-45м²: 30% (мода), 45-55м²: 14%, 55-70м²: 20%, 70-85м²: 8%, 85-100м²: 3%, более 100м²: 2%. В среднем около 49 м² (118.7М/2.4М). Это ВАЖНЕЙШИЙ benchmark для smart unit-mix Plan A","ДОМ.РФ распределение по комнатности: 1-комн 54% (1.31М), 2-комн 32% (0.78М), 3-комн 12% (0.30М), 4+комн 1% (33К)","ДОМ.РФ есть еще API endpoints: Единый реестр застройщиков, Единый реестр МЖК, Единый реестр ЖСК, Единый реестр проблемных объектов, Реализация квартир в строящихся домах (динамика продаж!), Распроданность и стройготовность (что-то вроде velocity!). Все по naash.dom.rf/аналитика/*. Велоситит и распроданность — могут быть прокси для нашего sales_tracker без необходимости парсить шахматки","🥈 ВЫВОД: для Plan A не нужно ждать ДДУ-данные от Антона. ДОМ.РФ открыто отдает агрегаты + ground truth по объектам. Это юридически чистый канал ('через API ДОМ.РФ' можно официально декларировать в реестре ПО)","Overpass API (OpenStreetMap, https://overpass-api.de/api/interpreter): бесплатно, без авторизации. Синтаксис Overpass QL: `[out:json][timeout:25]; node[\"amenity\"=\"school\"](56.7,60.5,56.9,60.7); out body;` — все школы в bbox. Поддерживает radius `node(around:1000)[\"amenity\"=\"hospital\"]`. Релевантные tags для нас: amenity=school|hospital|clinic|kindergarten|university, public_transport=station+station=subway|tram|bus, shop=mall|supermarket, leisure=park|playground, highway=primary|secondary","Overpass rate limit: ~5 запросов/мин на IP. Для production — поднять свой mirror через Docker (overpass-api/docker-overpass-api), либо использовать кеш Redis (TTL 24-72 ч, POI редко меняются)","Росстат fedstat.ru (id=31452): 'Средняя цена 1 кв.м общей площади квартир на рынке жилья' — квартально по регионам РФ, разбивка первичка/вторичка, по типам квартир. Данные с 2000 по 2025. Скачивание SDMX/возможно CSV/XML. НЕТ публичного API. Подход: разовая выгрузка → Excel → загрузить в нашу БД, обновлять раз в квартал вручную","Минстрой РФ (minstroyrf.gov.ru): норматив средней рыночной стоимости 1 м² для расчета субсидий. Публикуется приказом Минстроя ежеквартально по всем регионам. Для Свердл. обл. ~85-95 тыс ₽/м² на 2025 год. Доступ: PDF приказа на сайте Минстроя, копия в КонсультантПлюс. Использовать как baseline для нашей финмодели","Главгосэкспертиза НЦС (gge.ru, был 404 на /ncs/, реальный URL на момент проверки изменился): нормативы цены строительства 1 м² по типам зданий + регионам. Используется для расчета бюджетов госзаказа. Публикуется приказом Минстроя ежегодно. Для частных девелоперов — сравнительный baseline 'минимальная себестоимость'. Найти актуальный URL через минстроя/КонсультантПлюс","ЕРЗ.РФ (erzrf.ru): главный конкурент по аналитике в РФ, премиум-доступ. Полные данные: реестр застройщиков, рейтинги ЖК, статистика продаж, вакантность. Цена доступа на сайте не раскрыта — нужно запрашивать. Industry knowledge: ~50-100 тыс ₽/год за подписку на регион, ~500 тыс ₽/год за всю РФ. У них есть API B2B (под NDA). Использовать как 'договорённости в проработке' для инвесторов Раунда А, реально подписываться когда будет budget","СберИндекс (sberindex.ru, не открылся через WebFetch — 'unable to verify the first certificate', попробовать через браузер позже): бесплатные агрегаты Сбера по экономике РФ, включая жилье. Покрытие хорошее, обновление часто. Использовать для cross-validation с ДОМ.РФ + Росстат","ДомКлик (domclick.ru): listings первички и вторички + ИПОТЕЧНАЯ статистика. Открытый каталог с ценами/площадями. Анти-бот защита есть, но не очень жёсткая. Публичный sitemap.xml. Подход: спарсить раз в неделю свердл.обл (~50 тыс листингов), сохранить в БД с history. Для cross-check ДОМ.РФ цен","ЦИАН (cian.ru): крупнейший аггрегатор РФ. Cloudflare защита + rate limit. Парсить рискованно (юридически серая зона + блокировки). Готовые либы: cianparser, cianscraper на GitHub. Использовать ОЧЕНЬ ограниченно (200-500 листингов на район для калибровки), не как основной источник","Авито Недвижимость (avito.ru): аналогично ЦИАНу — антибот, Cloudflare. Есть платный B2B API (через avito_business — недешево). Для Discovery — пропускаем","Яндекс.Недвижимость (realty.ya.ru): listings, иногда XML feed для риелторских CRM. Есть Yandex Realty API (документированный, но enterprise). Для Discovery — пропускаем","Шахматки застройщиков (Брусника, Атомстройкомплекс и т.д.): по факту самый ground-truth источник для today snapshot. SPA-сайты — Playwright. SSR — httpx+BS4. План в Stage 2a/sales_tracker уже описан в SalesTracking_Strategy_Apr25. ОДНАКО — теперь когда у нас есть 'Реализация квартир' из ДОМ.РФ, шахматки могут быть downgrade-нуты до 'дополнительной валидации'","Другие источники (mention only): ОКТМО (для region IDs), ФИАС (адреса), Росреестр ПКК (только границы участков, без ДДУ), ДомРФ.Bank (ипотечные ставки — отдельный поток для финмодели), ЖКХ.МО.РФ (управляющие компании — для оценки операционных расходов). Всё в реестре российских данных.","ИТОГ — приоритеты по источникам для Discovery MVP (от 1 = критично до 5 = можно отложить):","1) ДОМ.РФ portal-analytics + api/kn/flats — бесплатно, открыто, ground truth, ВСЁ нужное для Plan A и Site Finder. Базовый источник","2) Overpass API (OSM) — бесплатно, для POI вокруг участков. Site Finder скоринг","3) Росстат fedstat.ru — квартальный baseline цен, разовая выгрузка → БД. Финмодель + cross-validation","4) Минстрой норматив + НЦС — для реалистичной себестоимости в финмодели","5) Шахматки 5-7 застройщиков Екбурга — дополнительная свежесть к ДОМ.РФ. Stage 2a опционально","Приоритет 1 ('вынес' ДДУ-данные): ВСЁ можно собрать сегодня вечером без Антона. Это разбивает блокировку #10 — даже если канал ДДУ серый/нет — Plan A 'analytics first' технически выполним на ДОМ.РФ data","Метрики, которые мы можем считать прямо из ДОМ.РФ для Plan A '3 плитки':","ПЛИТКА 1 — ЧТО СТРОИТЬ: для участка → район → ID региона → /portal-analytics/api/dashboard/by-room-count?regionId=X → распределение комнатности что строится. Дополнительно /dashboard/by-flat-area по тому же региону → распределение площадей. Smart unit-mix готов","ПЛИТКА 2 — ПО КАКОЙ ЦЕНЕ: для координат (lat,lon) → запрос /api/kn/flats/map в bbox 5km → собрать все priceMin → median + p10/p90 → диапазон цен в районе. Cross-check с Росстатом по региону","ПЛИТКА 3 — ЗА КАКОЕ ВРЕМЯ: /аналитика/реализация_строящихся_квартир + /распроданность-стройготовность по региону → absorption curves по комнатностям. Если бы у Антона были ДДУ — было бы точнее, но и без них есть прокси-сигнал","Юридическая безопасность для реестра ПО: всё что используем — ОФИЦИАЛЬНО открытые источники (ДОМ.РФ, Росстат, Минстрой, OSM). Никакого скрейпинга в обязательных каналах. Шахматки и ЦИАН — опциональные, помечаем как 'партнерские интеграции в проработке'","Implementation effort estimates (часы разработчика):","ДОМ.РФ analytics API клиент + кеш Redis: 4-6 часов","ДОМ.РФ flats/map per-объект клиент с пагинацией bbox: 6-8 часов","Overpass клиент + tag mapping для POI: 3-4 часа","Росстат разовая выгрузка + ETL в БД: 2-3 часа","Минстрой парсинг приказа PDF → таблица: 2-3 часа","СУММАРНО: 17-24 часа на полный data layer без Антона"]} +{"type":"entity","name":"DataSources_DomRF_Catalog_Apr26","entityType":"research","observations":["Дата: 26.04.2026, продолжение анализа ДОМ.РФ. Расширил understanding API после копки в /сервисы/api/kn/* и /portal-kn/api/*","🎯 ГЛАВНЫЙ EXTRACT-FLOW для всех ЖК Свердл. обл. в один проход:","ШАГ 1: GET /portal-kn/api/sales/portal?flatStatus=free,booked®ions=66 → возвращает плоский массив всех objId с продажами в регионе. Например, для Свердл. обл. ~100-200 ЖК","ШАГ 2: для каждого objId: GET /сервисы/api/kn/object?objId={id} → полная карточка ЖК","Header: Authorization: Basic MTpxd2U= (тот же 1:qwe). Запросов ~150-200 чтобы скачать всю Свердл. обл, при rate-limit 5 RPS — 30-40 сек на полный sync","Структура ответа /сервисы/api/kn/object — 30+ полей: hobjId, objId, developer{devId, shortName, fullName, groupName, orgForm, companyGroup, devInn}, rpdRegionCd, objAddr, shortAddr, metro{id,name,line,color,time,isWalk}, freeFlatsInfo{priceMin,numberFlats}, objCommercNm (название ЖК), objFloorMin/Max, objElemLivingCnt (всего квартир), objReady100PercDt (дата сдачи), objSquareLiving (общая жилая площадь), latitude, longitude, siteStatus, objGuarantyEscrowFlg, residentialBuildings, rpdNum, pdId (ID проектной декларации), buildType, objStatus, hobjRenderPhotoUrl (рендер ЖК), objPriceAVG (средняя цена м² по ЖК!), objPublDt, aiDescription (AI-генерация — у них уже работает GPT)","Реальный пример (Екб, ЖК «РАЗУМ на Малышева»): objId=65136, devInn=3000007315, lat/lon=56.8346/60.6396, метро Площадь 1905 года 27 мин, 413 квартир, 19089 м², дата сдачи 2028-12-31, средняя цена 180828 ₽/м², numberFlats свободных = 52","objPriceAVG — золотое поле. Это среднее между всеми квартирами в ЖК, обновляется регулярно. Лучше чем priceMin (минимальная цена), который часто означает 'студия 25 м²'. Для финмодели использовать objPriceAVG","freeFlatsInfo.numberFlats — динамика этого числа во времени = velocity proxy. Если ЖК утром было 60 свободных, вечером 52 — продали 8 квартир за день. Для Site Finder Velocity Score это ровно то что нужно. Парсить раз в сутки в нашу БД, считать delta","🛠 Endpoints which work (status 200, returning JSON):","GET /portal-analytics/api/dashboard/by-region (агрегаты по всей РФ)","GET /portal-analytics/api/dashboard/by-region?regionId=66 (фильтр)","GET /portal-analytics/api/dashboard/by-room-count (распределение комнатностей)","GET /portal-analytics/api/dashboard/by-flat-area (распределение площадей)","GET /portal-analytics/api/dashboard/by-developer (топ застройщиков)","GET /portal-analytics/api/dictionaries/regions","GET /portal-analytics/api/dictionaries/developers","GET /portal-kn/api/sales/portal?flatStatus=free,booked®ions=66 (массив ID ЖК)","GET /portal-kn/api/kn/places (справочник мест)","GET /portal-kn/api/kn/places?regionCd=66","GET /сервисы/api/kn/object?objId={id} (per-объект, ОСНОВНОЙ endpoint)","GET /сервисы/api/kn/flats/map?latitudePoint1=...&latitudePoint2=...&longitudePoint1=...&longitudePoint2=...&flatStatus=free,booked®ions=66 (per-ЖК с координатами в bbox)","GET /сервисы/api/kn/flats/filters (диапазоны цен/площадей/этажей)","GET /сервисы/api/kn/dataForFilters (справочник UI)","GET /сервисы/api/kn/meta?place=66&objStatus=0 (доступные значения mechanism, energyEff, wall, dduFlg etc)","GET /сервисы/api/kn/developers?objStatus=0&place=66&offset=0&limit=30&sortField=devShortCleanNm","GET /сервисы/api/kn/devGk?place=66 (группы компаний)","GET /сервисы/api/kn/commissioning?place=66&objStatus=0 (даты сдач)","GET /сервисы/api/kn/metro?place=66 (метро в регионе)","GET /сервисы/api/kn/hru/groups?place=66","🚫 Endpoints с проблемами (требуют доп. разбора):","/сервисы/api/kn/freeFlats?objId={id} → 502 (proxy error, может быть путь неверный)","/сервисы/api/kn/object/{id}/flats → 502","/portal-kn/api/object/{id} → 500 INTERNAL_ERROR","/portal-kn/api/object/{id}/flats → 500","Для получения per-flat данных (отдельные квартиры в ЖК с их площадью/комнатностью/ценой) — пока не нашли работающий endpoint. Поле freeFlatsInfo.numberFlats даёт лишь количество, не детали. Возможно нужно искать через /api/kn/realt/* или другие поддомены","📋 Базовый клиент-стратегия для нашего backend:","1) cron-task daily_dom_rf_sync: для каждого региона из списка целей → /sales/portal → массив objId → batch fetch /object → upsert в нашу таблицу dom_rf_objects. Время на ~1000 ЖК (вся Свердл.+МО для пилотов): ~3-5 мин. Кладем в Redis cache на 6 ч","2) cron-task daily_velocity_snapshot: пробежать те же objId, сохранить freeFlatsInfo.numberFlats с timestamp → таблица dom_rf_velocity_history (objId, snapshot_dt, free_count). Из истории считаем absorption rate","3) on-demand: при запросе пользователя про участок → bbox 5км → /flats/map → топ-20 ЖК-аналогов → /object для каждого → median(objPriceAVG), median(objSquareLiving/objElemLivingCnt), avg(objReady100PercDt - objPublDt) (срок строительства)","💡 Использование в трёх плитках Plan A:","ПЛИТКА «ЧТО СТРОИТЬ»: для региона по /by-region+/by-flat-area получаем benchmark distribution. Для конкретного района (5км вокруг участка) — через /flats/map берем все objId, получаем для каждого freeFlatsInfo.numberFlats и objSquareLiving/objElemLivingCnt. Считаем: средний размер квартиры в районе = objSquareLiving / objElemLivingCnt (для каждого ЖК), distribution по комнатностям через /by-room-count?regionId=X. Smart unit-mix готов для любого участка","ПЛИТКА «ПО КАКОЙ ЦЕНЕ»: median(objPriceAVG) по ЖК-аналогам в радиусе. Сравнить с baseline Росстата для региона. Sensitivity: спред p10-p90","ПЛИТКА «ЗА КАКОЕ ВРЕМЯ ПРОДАСТСЯ»: для каждого ЖК-аналога взять daily snapshot freeFlatsInfo.numberFlats за последние 30 дней (нужно собирать). Получить velocity (квартир/мес проданных). Спроектировать на наш случай: при N квартирах в концепте, при таком же темпе = время поглощения. До накопления истории — использовать /аналитика/реализация_строящихся_квартир (там может быть готовая агрегация)","🔐 Юридический статус ДОМ.РФ scraping: ДОМ.РФ — государственный портал, ЕИСЖС обязан публиковать эти данные согласно 214-ФЗ. JSON API публичный, captcha нет, robots.txt разрешает (надо проверить). Авторизация Basic MTpqd2U= — публичный 'технический' токен фронта. С точки зрения ТОС — это 'использование данных в формате, в котором они доступны на сайте'. Декларируем в реестре ПО как 'интеграция с ЕИСЖС ДОМ.РФ'. Юридически чисто","User-Agent: ставить осмысленный (`GenDesign-DataSync/0.1 (https://gendsgn.ru; ops@gendsgn.ru)`). Если ДОМ.РФ когда-нибудь решит закрыть API — есть legal-fallback через их официальную форму обратной связи (nash@domrf.ru) на B2B-доступ к 'сервисы для бизнеса'","📦 SCHEMA для миграции (когда будет Stage 2a):","table dom_rf_objects (obj_id PK, hobj_id, dev_id FK, dev_inn, group_name, region_cd, obj_addr, obj_short_addr, latitude, longitude, metro_id, metro_name, metro_time, obj_commerc_nm, obj_floor_min, obj_floor_max, obj_elem_living_cnt, obj_ready_100perc_dt, obj_square_living, obj_price_avg, obj_publ_dt, site_status, escrow_flg, problem_flg, building_type, ai_description, render_url, raw_payload jsonb, last_synced_at)","table dom_rf_developers (dev_id PK, dev_inn, short_name, full_name, group_name, company_group, org_form_id)","table dom_rf_velocity_snapshots (obj_id, snapshot_dt, free_count, price_min, PK(obj_id, snapshot_dt))","Индексы: GIST на (latitude, longitude) → ST_DWithin для радиус-запросов в районе"]} +{"type":"entity","name":"DataSources_DomRF_PerFlat_Apr26","entityType":"research","observations":["Дата: 26.04.2026 вечер. ОТКРЫТИЕ: ДОМ.РФ выдает per-квартира данные через GET /сервисы/api/kn/flats. До этого думали что только per-ЖК доступен","Endpoint: GET https://наш.дом.рф/сервисы/api/kn/flats?regions=66&offset=0&limit=N&flatStatus=free,booked","Auth: Authorization: Basic MTpqd2U= (тот же что для всех /сервисы/api/kn/*)","Параметры: regions (через запятую), offset, limit, flatStatus, + любые из 30+ фильтров (priceFrom/To, areaFrom/To, roomCount, type, energyEff, wall, hasParking, и т.д.)","По Свердл. обл. возвращает 24 441 квартиру в продаже/брони. Через offset/limit можно качать пачками (например limit=500, ~50 запросов на регион)","Структура per-flat (17 полей):","id (int) — глобальный ID квартиры в ЕИСЖС","odsId (string) — формат objId/sectionNum/flatNum (например '64057/2/55') — связь с реестром проектных деклараций","elemId (UUID) — токен для URL детальной страницы квартиры","type (string) — Квартира-студия / 1-комнатная / 2-комнатная / 3-комнатная / 4+комнатная / Апартаменты","number (string) — номер квартиры по застройщику","isStudio (bool)","totalArea (float) — общая площадь м² (это та что продаётся)","livingArea (float) — ЖИЛАЯ площадь м² (без коридоров/санузлов) — для финмодели важно отдельно","rooms (int) — количество комнат (для студии = 1)","status (enum) — free / booked / sold","price (int) — полная цена в ₽","pricePerSquareMeter (int) — цена м² в ₽","priceChangeDate (string DD-MM-YYYY HH:MM) — дата последнего изменения цены ⭐ КЛЮЧЕВОЕ ПОЛЕ для velocity","socialMortgage (bool) — доступна соц-ипотека","numberFloors (int) — этажность дома","floor (int) — этаж этой квартиры","image {attachmentId, attachmentIdSmall, hash, size, source, type} — type='layout' = планировка квартиры. URL для скачивания: /api/ext/file/{attachmentId}?type=png","objInfo {hobjId, objTransferPlanDt, objReady100PercDt, objCommercNm, objAddr} — связь с ЖК (плановая дата передачи + дата готовности + название + адрес)","🔥 priceChangeDate — это золотая жила. Снимая снапшот раз в день, мы видим:","1) Какие квартиры подешевели/подорожали (delta price)","2) Какие квартиры перешли в booked/sold (status changes)","3) Velocity по ЖК: кол-во квартир сменивших status за период","4) Эффективность ценовой стратегии: ЖК с активными priceChangeDate (последние 7 дней) vs ЖК где цены не двигались месяцами","Это ровно то что нужно для Plan A 'плитка 3 — за какое время продастся': вывожу absorption rate (квартир/мес) для ЖК-аналогов в радиусе 5 км → проектирую на наш концепт","🛠 ETL-стратегия для backend:","1) Daily full-refresh: для каждого региона из таргетов (66=Свердл, 50=МО, 77=Москва) → loop offset/limit с шагом 500 → upsert в таблицу dom_rf_flats","2) Daily delta tracking: SQL diff dom_rf_flats vs dom_rf_flats_yesterday → вычислить (a) кто перешёл в sold, (b) кому изменилась цена, (c) кто новый. Записать в dom_rf_flat_events","3) Materialized views: median(pricePerSquareMeter) по (region, type, year_quarter), absorption_rate по hobjId (sold quarters / 30 days)","📦 SQL schema для миграции (Stage 2a):","CREATE TABLE dom_rf_flats (id BIGINT PRIMARY KEY, ods_id VARCHAR(50), elem_id VARCHAR(64), obj_id INT, hobj_id INT, type VARCHAR(50), number VARCHAR(20), is_studio BOOL, rooms SMALLINT, total_area NUMERIC(8,2), living_area NUMERIC(8,2), price NUMERIC(12,2), price_per_sqm NUMERIC(10,2), price_change_dt TIMESTAMPTZ, status VARCHAR(20), social_mortgage BOOL, floor SMALLINT, num_floors SMALLINT, layout_attachment_id VARCHAR(80), obj_commerc_nm TEXT, obj_addr TEXT, obj_transfer_plan_dt TIMESTAMPTZ, obj_ready_dt TIMESTAMPTZ, last_synced_at TIMESTAMPTZ, raw JSONB, region_cd SMALLINT)","Индексы: (region_cd), (hobj_id), (status), (type), (price_per_sqm), (price_change_dt). Для радиус-запросов нужно JOIN с dom_rf_objects по hobj_id чтобы получить geom","CREATE TABLE dom_rf_flat_events (flat_id BIGINT, event_dt TIMESTAMPTZ, event_type VARCHAR(20), old_value JSONB, new_value JSONB, PRIMARY KEY(flat_id, event_dt)) -- тип event: 'status_change', 'price_change', 'new_listing', 'delisting'","💎 Что ещё можно подсосать через этот endpoint:","Скачивать ПЛАНИРОВКИ всех квартир (image.attachmentId, type='layout') — это набор ~24K image для Свердл. Можем использовать для AI-генерации (тренировка модели на схемах квартир) или показывать пользователю аналоги","objInfo.objTransferPlanDt vs objReady100PercDt — это плановая дата передачи квартир (когда фактически передают по ДДУ) vs дата готовности дома (когда строительство закончено). Лаг между ними обычно 3-6 мес и важен для absorption analysis","🔬 Качество данных: проверил на 2 квартирах из ЛО ('LIBERTY DOM'), цены 2.6М и 2.7М ₽, площадь 22 и 23 м² — реалистично. priceChangeDate '30-11-2025' = 5 мес. назад → значит эти квартиры в LIBERTY DOM плохо продаются (цена не двигалась)","Юридический статус (повтор): данные публикуются в ЕИСЖС в соответствии с 214-ФЗ как открытая информация по строящимся объектам. Использование в коммерческом продукте легально, для реестра ПО декларируем 'интеграция с ЕИСЖС ДОМ.РФ'","MCP-инструмент aim_memory_add_facts проверен 26.04.2026 вечер — добавление фактов через прямой вызов MCP вместо python-скриптов. На будущее: для всех записей в граф предпочтителен этот путь, скрипты только если MCP-сервер недоступен."]} {"type":"relation","from":"Project_GenDesign","to":"TechStack_v2.2","relationType":"uses"} {"type":"relation","from":"Project_GenDesign","to":"NorthStar_DiscoveryMVP","relationType":"targets"} {"type":"relation","from":"Project_GenDesign","to":"Phase0_DiscoveryMVP","relationType":"currently_executes"} @@ -32,30 +41,25 @@ {"type":"relation","from":"PlanA_AnalyticsFirst_Apr25","to":"SalesTracking_Strategy_Apr25","relationType":"depends_on"} {"type":"relation","from":"PlanA_AnalyticsFirst_Apr25","to":"DataSources_Pricing_Apr25","relationType":"depends_on"} {"type":"relation","from":"PlanA_AnalyticsFirst_Apr25","to":"Project_GenDesign","relationType":"reshapes"} -{"type": "entity", "name": "Domain_Setup_Apr26", "entityType": "infrastructure", "observations": ["Дата: 26.04.2026", "Домен: gendsgn.ru, регистрация в Beget, срок до 26.04.2027", "Стоимость: 199 ₽ первый год, 420 ₽/год продление. Сравнение из браузера 26.04.2026: Reg.ru 169₽ первый год + ~890₽ продление; Timeweb 200/399; nic.ru 747₽ регистрация (премиум)", "Решение: Beget — потому что (1) хостинг VPS уже там, единая админка и единая платежка для бухгалтерии; (2) продление 420₽ — лучшее на рынке; (3) free DNS включён", "DNS-серверы: ns1.beget.com, ns2.beget.com (плюс ns1.beget.pro, ns2.beget.pro как backup)", "A-записи прописаны 26.04.2026: @ (apex) и www → 46.173.16.127. Beget NS отдают корректно (проверено nslookup gendsgn.ru ns1.beget.com → 46.173.16.127). Публичные резолверы (8.8.8.8, 1.1.1.1) на 26.04 ещё NXDOMAIN — пропагация .ru registry → public DNS, ожидаемо 30 мин - 4 часа", "Caddyfile обновлён под gendsgn.ru: основной site block с auto-TLS Let's Encrypt, www → 301 redirect на apex, :80 fallback для IP-доступа сохранён. Закоммичен 68e95aa, запушен в main. На VM не применён (старый deploy.yml не делает git pull)", "deploy.yml будет дополнен `git fetch + reset --hard origin/main` + `caddy reload --config /etc/caddy/Caddyfile` после `up -d`. Закоммитим и запушим когда DNS пропагируется. Это финальный шаг для HTTPS", "Важное правило отрасли: с 1 сентября 2026 владелец .ru-домена обязан иметь подтверждённую учётку ЕСИА (Госуслуги) — иначе домен не продлят. Для ИП/ООО — через ЭДО/СБИС. Касается всех регистраторов одинаково. К моменту продления (26.04.2027) ООО уже должно быть зарегистрировано — учёт без проблем"]} -{"type": "entity", "name": "Sentry_Setup_Apr26", "entityType": "infrastructure", "observations": ["Дата: 26.04.2026", "Sentry организация: qqq (qqq-5y.sentry.io), free tier (5К ошибок + 10К транзакций / мес)", "Sentry проект: gendesign-backend, платформа FastAPI, регион Frankfurt (ingest.de.sentry.io)", "DSN сохранён в /opt/gendesign/backend/.env как SENTRY_DSN. На локальной машине — не нужен (Sentry init в backend/app/main.py условный — отключён если DSN пустой)", "Конфиг init: sentry_sdk.init(dsn=settings.sentry_dsn, environment=settings.environment, traces_sample_rate=0.1) — 10% запросов трейсятся для performance", "Алерты: «high priority» по умолчанию + email на alexandro361@gmail.com", "Тестовое событие отправлено и получено 26.04.2026 — event_id 67f05bdd59234a608a828fa81f72ded5. Sentry интеграция работает на проде", "Важная операционная заметка: docker compose restart НЕ перечитывает env_file. После правки backend/.env нужно `docker compose -f docker-compose.prod.yml up -d --force-recreate --no-deps backend`. Применимо к любым env-изменениям, не только Sentry", "DSN — не критичный секрет (в frontend SDK всегда зашит публично в JS-бандл). Держим в .env, не в репе. Ротация при необходимости через Sentry → Settings → Client Keys (DSN) → New Key"]} -{"type": "entity", "name": "OpsBundle_Apr26", "entityType": "infrastructure", "observations": ["Дата: 26.04.2026", "Подготовлен пакет инфраструктурных правок (в working tree, не закоммичено): Alembic baseline + TIGER cleanup + pre-commit + pg_dump backups + Makefile targets", "Alembic: создан backend/alembic.ini + backend/alembic/env.py (импорт Base из app.core.db, моделей через app.models.*) + script.py.mako + versions/.gitkeep. Первая реальная миграция — в Stage 2a, когда Parcel-модель будет финализирована. Сейчас versions/ намеренно пуст", "TIGER cleanup: backend/db/init/99_drop_unused_extensions.sql дропает postgis_tiger_geocoder + postgis_topology + fuzzystrmatch на свежем postgres-volume (через bind-mount ./backend/db/init:/docker-entrypoint-initdb.d:ro в обоих compose-файлах). Срабатывает только при первой инициализации пустого volume", "Pre-commit: .pre-commit-config.yaml в корне (trailing-whitespace, end-of-file-fixer, ruff lint+format на backend/, prettier на frontend/, detect-private-key, check-yaml/toml). Pre-commit добавлен в dev-deps backend/pyproject.toml. Локально — `python -m pre_commit install` или `make pre-commit-install`", "pg_dump: ops/backup.sh — cron-скрипт с опциональной выгрузкой в S3. Ретенция 7 дней локально. ops/restore.sh с защитой RESTORE_CONFIRM=yes", "Makefile: добавлены `make migration NAME=...` и `make pre-commit-install`. `make migrate` использует `alembic` напрямую (uv нет в production-образе)", "backend/Dockerfile: COPY alembic.ini + COPY alembic в обе стадии (builder и runner)", "S3 для бэкапов: пока не настроено. Когда настроим Selectel или Yandex Object Storage, env-конфиг — /etc/default/gendesign-backup на VM, cron — `30 3 * * * /usr/local/bin/gendesign-backup`"]} -{"type": "entity", "name": "Production_Wipe_Apr26", "entityType": "event", "observations": ["Дата: 26.04.2026", "Прод полностью пересоздан с нуля для чистого состояния. Не было критичных данных — БД содержала только postgis-defaults", "Команды на VM: `docker compose down -v` → `docker rmi` GHCR-образов и базовых (caddy:2, postgis/postgis:16-3.4, redis:7-alpine) → `git pull --ff-only` (Caddyfile, compose) → `docker system prune -af --volumes`", "Re-deploy через GitHub Actions Deploy → все 5 контейнеров поднялись с image-тегом 08a63f6 (главный коммит на момент wipe)", "Состояние после re-deploy: postgres healthy, redis healthy (новый healthcheck), backend healthy (фикс curl-healthcheck в multi-stage Dockerfile), frontend up, caddy up. PostGIS 3.4 USE_GEOS=1 USE_PROJ=1", "TIGER-мусор вернулся (как ожидалось) при свежей инициализации postgis-image. Будет автоматически удаляться при следующих wipe-ах когда применим OpsBundle_Apr26 (init-скрипт 99_drop_unused_extensions.sql)"]} -{"type": "entity", "name": "DockerOps_Apr26", "entityType": "infrastructure", "observations": ["Дата: 26.04.2026", "Серия Docker-фиксов задеплоена в main: e22aeb9 (multi-stage + npm ci + healthcheck + same-origin API), d0909e6 (ruff fixes), 08a63f6 (mypy fixes), 68e95aa (real domain Caddyfile)", "backend/Dockerfile: переписан на multi-stage (builder с build-essential + libgdal-dev + uv sync; runner только с runtime-либами libpq5, libgdal36, libproj25, libgeos-c1t64, libcairo2, libpango*, curl). Финальный образ 1.44 GB вместо ~2 GB. Запускается под non-root пользователем `app` (uid 1000)", "backend curl healthcheck в docker-compose.prod.yml теперь работает (до этого был баг — curl не был установлен в slim-образе)", "frontend/Dockerfile: переключён на `npm ci --legacy-peer-deps` вместо `npm install`. Запускается под `node` (uid 1000). package-lock.json закоммичен", "docker-compose.prod.yml + dev: убран dead NEXT_PUBLIC_API_BASE_URL (Next.js bakes NEXT_PUBLIC_* на build-time, runtime override no-op). Frontend ходит на same-origin /api/* — в проде через Caddy, в dev через Next.js rewrites (BACKEND_URL env)", "Caddyfile: исправлен баг — handle_path /api/* стрипал префикс /api, FastAPI router на /api/v1/* возвращал 404. Заменено на handle /api/*", "backend/uv.lock сгенерирован (через docker run python:3.12-slim + pip install uv && uv lock). Дальнейшие билды детерминированы: `uv sync --frozen --no-dev`", "Ruff config: ignore=['RUF001','RUF002','RUF003'] — проект русскоязычный, кириллица в комментариях/docstring (МСК-66 etc.) — норма, иначе постоянно ругается"]} -{"type": "relation", "from": "Domain_Setup_Apr26", "to": "Project_GenDesign", "relationType": "infrastructure_for"} -{"type": "relation", "from": "Domain_Setup_Apr26", "to": "Hosting_Decision_Apr25", "relationType": "extends"} -{"type": "relation", "from": "Sentry_Setup_Apr26", "to": "Project_GenDesign", "relationType": "monitors"} -{"type": "relation", "from": "Sentry_Setup_Apr26", "to": "Phase0_DiscoveryMVP", "relationType": "infrastructure_for"} -{"type": "relation", "from": "OpsBundle_Apr26", "to": "Project_GenDesign", "relationType": "infrastructure_for"} -{"type": "relation", "from": "OpsBundle_Apr26", "to": "Stages_MVP_v1", "relationType": "prepares_for"} -{"type": "relation", "from": "Production_Wipe_Apr26", "to": "Runbook_VPS_Setup_Beget", "relationType": "refreshed_state_of"} -{"type": "relation", "from": "DockerOps_Apr26", "to": "Runbook_VPS_Setup_Beget", "relationType": "extends"} -{"type": "entity", "name": "HTTPS_Live_Apr26", "entityType": "milestone", "observations": ["Дата: 26.04.2026, ~14:41 MSK", "Прод официально живой на HTTPS: https://gendsgn.ru/ возвращает 200, https://gendsgn.ru/health → {\"status\":\"ok\",\"environment\":\"prod\"}, https://www.gendsgn.ru/ → 301 на apex", "Цепочка: ЕСИА-верификация в Beget → реестр .ru обновился → DNS пропагировался к 8.8.8.8/1.1.1.1 → push deploy.yml с git-pull+caddy-reload не делался (юзер сделал руками на VM) → пришлось вычищать caddy_data volume чтобы сбросить старый retry-state на gendesign.example.ru и Let's Encrypt staging fallback → Caddy перезагрузился чисто → cert получен с production Let's Encrypt", "Урок: при wipe `down -v` мы убрали весь caddy volume включая ACME-state. Если бы оставили — Caddy продолжал бы фоновые retry-задачи на старый плейсхолдер gendesign.example.ru, и accumulated failures могли бы триггернуть staging fallback. На будущее: `caddy reload` не сбрасывает retry queue — для чистого старта нужен `restart` или volume-wipe", "GitHub-issues #1 (ЕСИА) и #2 (push deploy.yml) можно закрывать как done", "Стек живет: HTTP/2 + HTTP/3 (alt-svc), Caddy reverse-proxy фронт+бэк, FastAPI корректно отвечает через /api/* префикс (handle вместо handle_path сработал)"]} -{"type": "relation", "from": "HTTPS_Live_Apr26", "to": "Domain_Setup_Apr26", "relationType": "completes"} -{"type": "relation", "from": "HTTPS_Live_Apr26", "to": "Phase0_DiscoveryMVP", "relationType": "infrastructure_milestone_for"} -{"type": "relation", "from": "HTTPS_Live_Apr26", "to": "Project_GenDesign", "relationType": "deploys"} -{"type": "entity", "name": "DataSources_Analysis_Apr26", "entityType": "research", "observations": ["Дата: 26.04.2026. Глубокий анализ источников данных для GenDesign — проверено через debug-браузер на наш.дом.рф", "🥇 ГЛАВНАЯ НАХОДКА: ДОМ.РФ имеет ОТКРЫТЫЕ JSON API без аутентификации. Это меняет всё для Plan A и Site Finder", "ДОМ.РФ Аналитика API (`naash.dom.rf/portal-analytics/api/`): /dictionaries/regions, /dictionaries/developers, /dashboard/by-room-count, /dashboard/by-flat-area, /dashboard/by-developer, /dashboard/by-region. Возвращают JSON: по каждому региону id, flatCount, area, и details — разбивка по комнатности (ONE/TWO/THREE/FOUR с площадями и %). Базовый URL хоста: https://xn--80az8a.xn--d1aqf.xn--p1ai (он же наш.дом.рф). Без auth, accept: application/json. Свердл. обл. id=66, flatCount=115205, area=5.67M м²", "ДОМ.РФ Каталог квартир API (`/сервисы/api/kn/`): /flats/map?latitudePoint1=...&latitudePoint2=...&longitudePoint1=...&longitudePoint2=...&flatStatus=free,booked возвращает per-ЖК записи {objId, latitude, longitude, priceMin, objStatus}. /flats/filters даёт диапазоны фильтров. Auth: header `Authorization: Basic MTpqd2U=` (= base64('1:qwe')) — хардкод в фронте, действующий ключ. По Свердл. обл. видно 24 441 квартир в продаже", "ДОМ.РФ доступные на per-flat фильтры (которые присутствуют в JSON): кол-во комнат (студия/1/2/3/4+), год сдачи (Сдан/2026/2027/2028+), цена (от 2.6М до 4.8 млрд), площадь (16-1001 м²), этаж (1-68), цена м² (82К-7.2М), отделка (без/черновая/предчистовая/чистовая/под ключ), класс (типовой/комфорт/бизнес/элитный), материал стен (блоки/кирпич/панель/монолит/монолит-кирпич), тип (жилое/нежилое), эскроу, безбарьерная среда, благоустройство, энергоэффективность, наличие апартаментов", "ДОМ.РФ покрытие России: 2.4 млн квартир в строящихся домах, 118.7 млн м². По регионам ТОП-20 включает Свердл.обл (4-е место, 5.67М м²). По девелоперам ТОП-15: Самолет, ПИК, DOGMA, ТОЧНО, ФСК, Брусника, Setl Group, ЛСР и др.", "ДОМ.РФ распределение площадей квартир в РФ: 0-25м²: 6%, 25-35м²: 17%, 35-45м²: 30% (мода), 45-55м²: 14%, 55-70м²: 20%, 70-85м²: 8%, 85-100м²: 3%, более 100м²: 2%. В среднем около 49 м² (118.7М/2.4М). Это ВАЖНЕЙШИЙ benchmark для smart unit-mix Plan A", "ДОМ.РФ распределение по комнатности: 1-комн 54% (1.31М), 2-комн 32% (0.78М), 3-комн 12% (0.30М), 4+комн 1% (33К)", "ДОМ.РФ есть еще API endpoints: Единый реестр застройщиков, Единый реестр МЖК, Единый реестр ЖСК, Единый реестр проблемных объектов, Реализация квартир в строящихся домах (динамика продаж!), Распроданность и стройготовность (что-то вроде velocity!). Все по naash.dom.rf/аналитика/*. Велоситит и распроданность — могут быть прокси для нашего sales_tracker без необходимости парсить шахматки", "🥈 ВЫВОД: для Plan A не нужно ждать ДДУ-данные от Антона. ДОМ.РФ открыто отдает агрегаты + ground truth по объектам. Это юридически чистый канал ('через API ДОМ.РФ' можно официально декларировать в реестре ПО)", "Overpass API (OpenStreetMap, https://overpass-api.de/api/interpreter): бесплатно, без авторизации. Синтаксис Overpass QL: `[out:json][timeout:25]; node[\"amenity\"=\"school\"](56.7,60.5,56.9,60.7); out body;` — все школы в bbox. Поддерживает radius `node(around:1000)[\"amenity\"=\"hospital\"]`. Релевантные tags для нас: amenity=school|hospital|clinic|kindergarten|university, public_transport=station+station=subway|tram|bus, shop=mall|supermarket, leisure=park|playground, highway=primary|secondary", "Overpass rate limit: ~5 запросов/мин на IP. Для production — поднять свой mirror через Docker (overpass-api/docker-overpass-api), либо использовать кеш Redis (TTL 24-72 ч, POI редко меняются)", "Росстат fedstat.ru (id=31452): 'Средняя цена 1 кв.м общей площади квартир на рынке жилья' — квартально по регионам РФ, разбивка первичка/вторичка, по типам квартир. Данные с 2000 по 2025. Скачивание SDMX/возможно CSV/XML. НЕТ публичного API. Подход: разовая выгрузка → Excel → загрузить в нашу БД, обновлять раз в квартал вручную", "Минстрой РФ (minstroyrf.gov.ru): норматив средней рыночной стоимости 1 м² для расчета субсидий. Публикуется приказом Минстроя ежеквартально по всем регионам. Для Свердл. обл. ~85-95 тыс ₽/м² на 2025 год. Доступ: PDF приказа на сайте Минстроя, копия в КонсультантПлюс. Использовать как baseline для нашей финмодели", "Главгосэкспертиза НЦС (gge.ru, был 404 на /ncs/, реальный URL на момент проверки изменился): нормативы цены строительства 1 м² по типам зданий + регионам. Используется для расчета бюджетов госзаказа. Публикуется приказом Минстроя ежегодно. Для частных девелоперов — сравнительный baseline 'минимальная себестоимость'. Найти актуальный URL через минстроя/КонсультантПлюс", "ЕРЗ.РФ (erzrf.ru): главный конкурент по аналитике в РФ, премиум-доступ. Полные данные: реестр застройщиков, рейтинги ЖК, статистика продаж, вакантность. Цена доступа на сайте не раскрыта — нужно запрашивать. Industry knowledge: ~50-100 тыс ₽/год за подписку на регион, ~500 тыс ₽/год за всю РФ. У них есть API B2B (под NDA). Использовать как 'договорённости в проработке' для инвесторов Раунда А, реально подписываться когда будет budget", "СберИндекс (sberindex.ru, не открылся через WebFetch — 'unable to verify the first certificate', попробовать через браузер позже): бесплатные агрегаты Сбера по экономике РФ, включая жилье. Покрытие хорошее, обновление часто. Использовать для cross-validation с ДОМ.РФ + Росстат", "ДомКлик (domclick.ru): listings первички и вторички + ИПОТЕЧНАЯ статистика. Открытый каталог с ценами/площадями. Анти-бот защита есть, но не очень жёсткая. Публичный sitemap.xml. Подход: спарсить раз в неделю свердл.обл (~50 тыс листингов), сохранить в БД с history. Для cross-check ДОМ.РФ цен", "ЦИАН (cian.ru): крупнейший аггрегатор РФ. Cloudflare защита + rate limit. Парсить рискованно (юридически серая зона + блокировки). Готовые либы: cianparser, cianscraper на GitHub. Использовать ОЧЕНЬ ограниченно (200-500 листингов на район для калибровки), не как основной источник", "Авито Недвижимость (avito.ru): аналогично ЦИАНу — антибот, Cloudflare. Есть платный B2B API (через avito_business — недешево). Для Discovery — пропускаем", "Яндекс.Недвижимость (realty.ya.ru): listings, иногда XML feed для риелторских CRM. Есть Yandex Realty API (документированный, но enterprise). Для Discovery — пропускаем", "Шахматки застройщиков (Брусника, Атомстройкомплекс и т.д.): по факту самый ground-truth источник для today snapshot. SPA-сайты — Playwright. SSR — httpx+BS4. План в Stage 2a/sales_tracker уже описан в SalesTracking_Strategy_Apr25. ОДНАКО — теперь когда у нас есть 'Реализация квартир' из ДОМ.РФ, шахматки могут быть downgrade-нуты до 'дополнительной валидации'", "Другие источники (mention only): ОКТМО (для region IDs), ФИАС (адреса), Росреестр ПКК (только границы участков, без ДДУ), ДомРФ.Bank (ипотечные ставки — отдельный поток для финмодели), ЖКХ.МО.РФ (управляющие компании — для оценки операционных расходов). Всё в реестре российских данных.", "ИТОГ — приоритеты по источникам для Discovery MVP (от 1 = критично до 5 = можно отложить):", "1) ДОМ.РФ portal-analytics + api/kn/flats — бесплатно, открыто, ground truth, ВСЁ нужное для Plan A и Site Finder. Базовый источник", "2) Overpass API (OSM) — бесплатно, для POI вокруг участков. Site Finder скоринг", "3) Росстат fedstat.ru — квартальный baseline цен, разовая выгрузка → БД. Финмодель + cross-validation", "4) Минстрой норматив + НЦС — для реалистичной себестоимости в финмодели", "5) Шахматки 5-7 застройщиков Екбурга — дополнительная свежесть к ДОМ.РФ. Stage 2a опционально", "Приоритет 1 ('вынес' ДДУ-данные): ВСЁ можно собрать сегодня вечером без Антона. Это разбивает блокировку #10 — даже если канал ДДУ серый/нет — Plan A 'analytics first' технически выполним на ДОМ.РФ data", "Метрики, которые мы можем считать прямо из ДОМ.РФ для Plan A '3 плитки':", "ПЛИТКА 1 — ЧТО СТРОИТЬ: для участка → район → ID региона → /portal-analytics/api/dashboard/by-room-count?regionId=X → распределение комнатности что строится. Дополнительно /dashboard/by-flat-area по тому же региону → распределение площадей. Smart unit-mix готов", "ПЛИТКА 2 — ПО КАКОЙ ЦЕНЕ: для координат (lat,lon) → запрос /api/kn/flats/map в bbox 5km → собрать все priceMin → median + p10/p90 → диапазон цен в районе. Cross-check с Росстатом по региону", "ПЛИТКА 3 — ЗА КАКОЕ ВРЕМЯ: /аналитика/реализация_строящихся_квартир + /распроданность-стройготовность по региону → absorption curves по комнатностям. Если бы у Антона были ДДУ — было бы точнее, но и без них есть прокси-сигнал", "Юридическая безопасность для реестра ПО: всё что используем — ОФИЦИАЛЬНО открытые источники (ДОМ.РФ, Росстат, Минстрой, OSM). Никакого скрейпинга в обязательных каналах. Шахматки и ЦИАН — опциональные, помечаем как 'партнерские интеграции в проработке'", "Implementation effort estimates (часы разработчика):", "ДОМ.РФ analytics API клиент + кеш Redis: 4-6 часов", "ДОМ.РФ flats/map per-объект клиент с пагинацией bbox: 6-8 часов", "Overpass клиент + tag mapping для POI: 3-4 часа", "Росстат разовая выгрузка + ETL в БД: 2-3 часа", "Минстрой парсинг приказа PDF → таблица: 2-3 часа", "СУММАРНО: 17-24 часа на полный data layer без Антона"]} -{"type": "relation", "from": "DataSources_Analysis_Apr26", "to": "DataSources_Pricing_Apr25", "relationType": "supersedes"} -{"type": "relation", "from": "DataSources_Analysis_Apr26", "to": "PlanA_AnalyticsFirst_Apr25", "relationType": "unblocks"} -{"type": "relation", "from": "DataSources_Analysis_Apr26", "to": "SalesTracking_Strategy_Apr25", "relationType": "downgrades"} -{"type": "relation", "from": "DataSources_Analysis_Apr26", "to": "Stages_MVP_v1", "relationType": "amends_stage_2a"} -{"type": "relation", "from": "DataSources_Analysis_Apr26", "to": "Project_GenDesign", "relationType": "data_strategy_for"} -{"type": "entity", "name": "DataSources_DomRF_Catalog_Apr26", "entityType": "research", "observations": ["Дата: 26.04.2026, продолжение анализа ДОМ.РФ. Расширил understanding API после копки в /сервисы/api/kn/* и /portal-kn/api/*", "🎯 ГЛАВНЫЙ EXTRACT-FLOW для всех ЖК Свердл. обл. в один проход:", "ШАГ 1: GET /portal-kn/api/sales/portal?flatStatus=free,booked®ions=66 → возвращает плоский массив всех objId с продажами в регионе. Например, для Свердл. обл. ~100-200 ЖК", "ШАГ 2: для каждого objId: GET /сервисы/api/kn/object?objId={id} → полная карточка ЖК", "Header: Authorization: Basic MTpxd2U= (тот же 1:qwe). Запросов ~150-200 чтобы скачать всю Свердл. обл, при rate-limit 5 RPS — 30-40 сек на полный sync", "Структура ответа /сервисы/api/kn/object — 30+ полей: hobjId, objId, developer{devId, shortName, fullName, groupName, orgForm, companyGroup, devInn}, rpdRegionCd, objAddr, shortAddr, metro{id,name,line,color,time,isWalk}, freeFlatsInfo{priceMin,numberFlats}, objCommercNm (название ЖК), objFloorMin/Max, objElemLivingCnt (всего квартир), objReady100PercDt (дата сдачи), objSquareLiving (общая жилая площадь), latitude, longitude, siteStatus, objGuarantyEscrowFlg, residentialBuildings, rpdNum, pdId (ID проектной декларации), buildType, objStatus, hobjRenderPhotoUrl (рендер ЖК), objPriceAVG (средняя цена м² по ЖК!), objPublDt, aiDescription (AI-генерация — у них уже работает GPT)", "Реальный пример (Екб, ЖК «РАЗУМ на Малышева»): objId=65136, devInn=3000007315, lat/lon=56.8346/60.6396, метро Площадь 1905 года 27 мин, 413 квартир, 19089 м², дата сдачи 2028-12-31, средняя цена 180828 ₽/м², numberFlats свободных = 52", "objPriceAVG — золотое поле. Это среднее между всеми квартирами в ЖК, обновляется регулярно. Лучше чем priceMin (минимальная цена), который часто означает 'студия 25 м²'. Для финмодели использовать objPriceAVG", "freeFlatsInfo.numberFlats — динамика этого числа во времени = velocity proxy. Если ЖК утром было 60 свободных, вечером 52 — продали 8 квартир за день. Для Site Finder Velocity Score это ровно то что нужно. Парсить раз в сутки в нашу БД, считать delta", "🛠 Endpoints which work (status 200, returning JSON):", "GET /portal-analytics/api/dashboard/by-region (агрегаты по всей РФ)", "GET /portal-analytics/api/dashboard/by-region?regionId=66 (фильтр)", "GET /portal-analytics/api/dashboard/by-room-count (распределение комнатностей)", "GET /portal-analytics/api/dashboard/by-flat-area (распределение площадей)", "GET /portal-analytics/api/dashboard/by-developer (топ застройщиков)", "GET /portal-analytics/api/dictionaries/regions", "GET /portal-analytics/api/dictionaries/developers", "GET /portal-kn/api/sales/portal?flatStatus=free,booked®ions=66 (массив ID ЖК)", "GET /portal-kn/api/kn/places (справочник мест)", "GET /portal-kn/api/kn/places?regionCd=66", "GET /сервисы/api/kn/object?objId={id} (per-объект, ОСНОВНОЙ endpoint)", "GET /сервисы/api/kn/flats/map?latitudePoint1=...&latitudePoint2=...&longitudePoint1=...&longitudePoint2=...&flatStatus=free,booked®ions=66 (per-ЖК с координатами в bbox)", "GET /сервисы/api/kn/flats/filters (диапазоны цен/площадей/этажей)", "GET /сервисы/api/kn/dataForFilters (справочник UI)", "GET /сервисы/api/kn/meta?place=66&objStatus=0 (доступные значения mechanism, energyEff, wall, dduFlg etc)", "GET /сервисы/api/kn/developers?objStatus=0&place=66&offset=0&limit=30&sortField=devShortCleanNm", "GET /сервисы/api/kn/devGk?place=66 (группы компаний)", "GET /сервисы/api/kn/commissioning?place=66&objStatus=0 (даты сдач)", "GET /сервисы/api/kn/metro?place=66 (метро в регионе)", "GET /сервисы/api/kn/hru/groups?place=66", "🚫 Endpoints с проблемами (требуют доп. разбора):", "/сервисы/api/kn/freeFlats?objId={id} → 502 (proxy error, может быть путь неверный)", "/сервисы/api/kn/object/{id}/flats → 502", "/portal-kn/api/object/{id} → 500 INTERNAL_ERROR", "/portal-kn/api/object/{id}/flats → 500", "Для получения per-flat данных (отдельные квартиры в ЖК с их площадью/комнатностью/ценой) — пока не нашли работающий endpoint. Поле freeFlatsInfo.numberFlats даёт лишь количество, не детали. Возможно нужно искать через /api/kn/realt/* или другие поддомены", "📋 Базовый клиент-стратегия для нашего backend:", "1) cron-task daily_dom_rf_sync: для каждого региона из списка целей → /sales/portal → массив objId → batch fetch /object → upsert в нашу таблицу dom_rf_objects. Время на ~1000 ЖК (вся Свердл.+МО для пилотов): ~3-5 мин. Кладем в Redis cache на 6 ч", "2) cron-task daily_velocity_snapshot: пробежать те же objId, сохранить freeFlatsInfo.numberFlats с timestamp → таблица dom_rf_velocity_history (objId, snapshot_dt, free_count). Из истории считаем absorption rate", "3) on-demand: при запросе пользователя про участок → bbox 5км → /flats/map → топ-20 ЖК-аналогов → /object для каждого → median(objPriceAVG), median(objSquareLiving/objElemLivingCnt), avg(objReady100PercDt - objPublDt) (срок строительства)", "💡 Использование в трёх плитках Plan A:", "ПЛИТКА «ЧТО СТРОИТЬ»: для региона по /by-region+/by-flat-area получаем benchmark distribution. Для конкретного района (5км вокруг участка) — через /flats/map берем все objId, получаем для каждого freeFlatsInfo.numberFlats и objSquareLiving/objElemLivingCnt. Считаем: средний размер квартиры в районе = objSquareLiving / objElemLivingCnt (для каждого ЖК), distribution по комнатностям через /by-room-count?regionId=X. Smart unit-mix готов для любого участка", "ПЛИТКА «ПО КАКОЙ ЦЕНЕ»: median(objPriceAVG) по ЖК-аналогам в радиусе. Сравнить с baseline Росстата для региона. Sensitivity: спред p10-p90", "ПЛИТКА «ЗА КАКОЕ ВРЕМЯ ПРОДАСТСЯ»: для каждого ЖК-аналога взять daily snapshot freeFlatsInfo.numberFlats за последние 30 дней (нужно собирать). Получить velocity (квартир/мес проданных). Спроектировать на наш случай: при N квартирах в концепте, при таком же темпе = время поглощения. До накопления истории — использовать /аналитика/реализация_строящихся_квартир (там может быть готовая агрегация)", "🔐 Юридический статус ДОМ.РФ scraping: ДОМ.РФ — государственный портал, ЕИСЖС обязан публиковать эти данные согласно 214-ФЗ. JSON API публичный, captcha нет, robots.txt разрешает (надо проверить). Авторизация Basic MTpqd2U= — публичный 'технический' токен фронта. С точки зрения ТОС — это 'использование данных в формате, в котором они доступны на сайте'. Декларируем в реестре ПО как 'интеграция с ЕИСЖС ДОМ.РФ'. Юридически чисто", "User-Agent: ставить осмысленный (`GenDesign-DataSync/0.1 (https://gendsgn.ru; ops@gendsgn.ru)`). Если ДОМ.РФ когда-нибудь решит закрыть API — есть legal-fallback через их официальную форму обратной связи (nash@domrf.ru) на B2B-доступ к 'сервисы для бизнеса'", "📦 SCHEMA для миграции (когда будет Stage 2a):", "table dom_rf_objects (obj_id PK, hobj_id, dev_id FK, dev_inn, group_name, region_cd, obj_addr, obj_short_addr, latitude, longitude, metro_id, metro_name, metro_time, obj_commerc_nm, obj_floor_min, obj_floor_max, obj_elem_living_cnt, obj_ready_100perc_dt, obj_square_living, obj_price_avg, obj_publ_dt, site_status, escrow_flg, problem_flg, building_type, ai_description, render_url, raw_payload jsonb, last_synced_at)", "table dom_rf_developers (dev_id PK, dev_inn, short_name, full_name, group_name, company_group, org_form_id)", "table dom_rf_velocity_snapshots (obj_id, snapshot_dt, free_count, price_min, PK(obj_id, snapshot_dt))", "Индексы: GIST на (latitude, longitude) → ST_DWithin для радиус-запросов в районе"]} -{"type": "relation", "from": "DataSources_DomRF_Catalog_Apr26", "to": "DataSources_Analysis_Apr26", "relationType": "extends"} -{"type": "relation", "from": "DataSources_DomRF_Catalog_Apr26", "to": "PlanA_AnalyticsFirst_Apr25", "relationType": "operationalizes"} -{"type": "relation", "from": "DataSources_DomRF_Catalog_Apr26", "to": "Stages_MVP_v1", "relationType": "amends_stage_2a"} +{"type":"relation","from":"Domain_Setup_Apr26","to":"Project_GenDesign","relationType":"infrastructure_for"} +{"type":"relation","from":"Domain_Setup_Apr26","to":"Hosting_Decision_Apr25","relationType":"extends"} +{"type":"relation","from":"Sentry_Setup_Apr26","to":"Project_GenDesign","relationType":"monitors"} +{"type":"relation","from":"Sentry_Setup_Apr26","to":"Phase0_DiscoveryMVP","relationType":"infrastructure_for"} +{"type":"relation","from":"OpsBundle_Apr26","to":"Project_GenDesign","relationType":"infrastructure_for"} +{"type":"relation","from":"OpsBundle_Apr26","to":"Stages_MVP_v1","relationType":"prepares_for"} +{"type":"relation","from":"Production_Wipe_Apr26","to":"Runbook_VPS_Setup_Beget","relationType":"refreshed_state_of"} +{"type":"relation","from":"DockerOps_Apr26","to":"Runbook_VPS_Setup_Beget","relationType":"extends"} +{"type":"relation","from":"HTTPS_Live_Apr26","to":"Domain_Setup_Apr26","relationType":"completes"} +{"type":"relation","from":"HTTPS_Live_Apr26","to":"Phase0_DiscoveryMVP","relationType":"infrastructure_milestone_for"} +{"type":"relation","from":"HTTPS_Live_Apr26","to":"Project_GenDesign","relationType":"deploys"} +{"type":"relation","from":"DataSources_Analysis_Apr26","to":"DataSources_Pricing_Apr25","relationType":"supersedes"} +{"type":"relation","from":"DataSources_Analysis_Apr26","to":"PlanA_AnalyticsFirst_Apr25","relationType":"unblocks"} +{"type":"relation","from":"DataSources_Analysis_Apr26","to":"SalesTracking_Strategy_Apr25","relationType":"downgrades"} +{"type":"relation","from":"DataSources_Analysis_Apr26","to":"Stages_MVP_v1","relationType":"amends_stage_2a"} +{"type":"relation","from":"DataSources_Analysis_Apr26","to":"Project_GenDesign","relationType":"data_strategy_for"} +{"type":"relation","from":"DataSources_DomRF_Catalog_Apr26","to":"DataSources_Analysis_Apr26","relationType":"extends"} +{"type":"relation","from":"DataSources_DomRF_Catalog_Apr26","to":"PlanA_AnalyticsFirst_Apr25","relationType":"operationalizes"} +{"type":"relation","from":"DataSources_DomRF_Catalog_Apr26","to":"Stages_MVP_v1","relationType":"amends_stage_2a"} +{"type":"relation","from":"DataSources_DomRF_PerFlat_Apr26","to":"DataSources_DomRF_Catalog_Apr26","relationType":"extends"} +{"type":"relation","from":"DataSources_DomRF_PerFlat_Apr26","to":"PlanA_AnalyticsFirst_Apr25","relationType":"fully_unblocks"} +{"type":"relation","from":"DataSources_DomRF_PerFlat_Apr26","to":"SalesTracking_Strategy_Apr25","relationType":"supersedes"}