Контекст экосистемы для ИИ-агента¶
Точка входа для агента, работающего над задачами, которые выходят за пределы
одного репозитория. Здесь то, чего не видно из кода soniks-client:
устройство соседних проектов, контракты между ними и подтверждённые дефекты на
их стороне.
Что здесь не дублируется: устройство самого клиента — оно в
docs/development/; правила работы с этим
репозиторием — они в CLAUDE.md; план работ — он в
Дорожная карта: надёжность сети.
Как этим пользоваться¶
Читать целиком, если задача касается портала, флоуграфов, обновления станций или приёма данных. Пропустить, если правите изолированный кусок клиента.
Все факты ниже проверены чтением кода в даты и на коммитах из таблицы «Снимок». Соседние репозитории живут своей жизнью и коммитятся другими людьми — прежде чем опираться на конкретный номер строки, перечитайте это место. Утверждения о поведении держатся дольше, чем координаты.
Карта экосистемы¶
Шесть репозиториев, все в одном родительском каталоге:
Репозиторий |
Что |
Реальное состояние |
|---|---|---|
|
клиент станции, Python 3.11 |
этот репозиторий; до 2026-09-14 — |
|
флоуграфы GNU Radio + |
форк |
|
форк |
чистое зеркало: |
|
портал, Python 3.12 / Django 5.2 LTS (с 2026-09-02) |
форк |
|
монитор станции, 1357 строк |
студенческий, в личном репозитории |
|
агент обновлений станции, Python 3.11 |
написан 2026-09-11 (агент v1, решения 38–44 и 60–66 дорожной карты): сервис того же compose-проекта, что клиент; читает |
Границы работы¶
Правило CLAUDE.md — работать только внутри корня репозитория — остаётся в силе
по умолчанию. Чтение соседних репозиториев допустимо только когда человек явно
дал доступ в текущей сессии. Записывать в них — только по отдельной просьбе.
Корневой .env — живая станция мейнтейнера с рабочим токеном. Не читать, не
логировать. Шаблон для правок — client/.env.
Портал: то, что нужно знать клиенту¶
Стек (с 2026-09-02): Python 3.12, Django 5.2 LTS, DRF 3.18, Celery 5.2,
PostgreSQL 15, Redis 7.4, Debian bookworm. WSGI, gunicorn. Ни channels, ни
WebSocket в самом Django. Push к станции — с 2026-09-10 через MQTT: рядом с
порталом стоит Mosquitto (mosquitto-go-auth, профиль compose mqtt), nginx
отдаёт его как wss://sonik.space/mqtt; портал публикует документ состояния
станции при сохранении формы, мост manage.py mqtt_bridge пишет статусы
станций в БД. REST остаётся источником истины и фолбэком (решение 47
дорожной карты).
Артефакты наблюдений лежат на файловой системе (MEDIA_ROOT), S3-флаги по
умолчанию выключены. bin/djangoctl.sh запускает gunicorn с nproc*2+1
воркерами и таймаутом 600 с (GUNICORN_WORKERS, GUNICORN_TIMEOUT в .env
сервера переопределяют). ~~Реальный скрипт в продакшне подменяется из CI~~ —
с 2026-08-27 не подменяется, а с 2026-09-02 образ портала собирается в CI и
выкатка тянет его из registry GitLab: репозиторная версия и есть прод.
Пять запросов, которыми живёт станция¶
Запрос |
Что делает |
|---|---|
|
расписание. Он же единственный heartbeat: |
|
метаданные прохода |
|
один артефакт за запрос: |
|
sat-data bundle, анонимно ( |
|
с 2026-09-10: |
|
с 2026-09-10: документ состояния — |
MQTT |
с 2026-09-10 ( |
Клиентская сторона — src/soniks_client/api.py (расписание, статус,
выгрузка), src/soniks_client/remote_config.py (состояние, через сессию
api), src/soniks_client/mqtt.py (брокер) и src/soniks_client/sat_data.py
(bundle, своя requests.Session); других сетевых вызовов в src/ нет.
Что отдаёт /api/jobs/, описано машинно. С 2026-09-02 портал держит
openapi.yml в soniks-network-api-client/ и сверяет его с кодом в CI на
каждом пуше. Для вопроса «какие поля и какого типа приходят в задании» это
первоисточник; читать сериализатор нужно только чтобы понять, почему.
Подтверждённые дефекты на стороне портала¶
Проверено чтением кода. Влияют на клиент напрямую, поэтому перечислены здесь, а не только в роадмапе.
~~rate_observation считает Good любой кадр.~~ Закрыто 2026-08-24
(Фаза 0.16). Было: observation.transmitter.downlink_mode not in ["CW", "FM"]
сравнивало ForeignKey со строками и было истинно всегда, любой кадр давал
status = 100. Теперь оценка читает строку Observation.transmitter_mode —
снимок передатчика на момент планирования (с 2026-08-28 наблюдение хранит
такие снимки для всех transmitter_* и station_*). История оценок на
проде не пересчитана и не будет — пересчёт на паузе решением мейнтейнера
2026-09-10. Значит success_rate за период до починки завышен, и это
принято: любой вывод об успешности наблюдений строить на данных после
выкатки правки портала.
GET /api/jobs/ открыт анонимно — по умолчанию. JobView.get_permissions()
отдаёт AllowAny при JOBS_ALLOW_ANONYMOUS=True (умолчание) и
IsAuthenticated при False; каждый анонимный запрос с ground_station
пишется в лог предупреждением — это счётчик готовности парка к переключению.
Расписание всей сети читается кем угодно без токена, пока флаг не переключён.
Предупреждение
Отсюда раньше делался вывод «станция с протухшим токеном продолжит получать
задания, но будет числиться Offline». Он неверен. Клиент шлёт
Authorization: Token … (src/soniks_client/api.py:40), а DRF
TokenAuthentication на невалидный токен отвечает 401 до проверки прав,
независимо от permission_classes. Такая станция ослепла уже сегодня.
Анонимный доступ — про третьих лиц, а не про парк.
Токен пользовательский, а не станционный. Создаётся сигналом post_save на
User (network/users/models.py). Один токен на все станции владельца;
ротация из UI молча убивает heartbeat всего парка (с 2026-08-29 она хотя бы
только по POST — до этого её вызывал GET, то есть любая чужая страница).
Класс ClientIDAuthentication (network/api/authentication.py) написан для
станционной аутентификации и никуда не подключён — на 2026-09-10 тоже;
почему это не одна строка, разобрано в дорожной карте (Фаза 1, «Здоровье и
идентичность»).
Справочник режимов пополняется автоматически из фида SatNOGS DB
(network/base/tasks.py:489) и ни с чем не сверяется. Портал может запланировать
режим, которого нет в таблице клиента, — с 2026-09-10 только станции, которая
режимы не объявила (решение 31); объявившая такой проход отказывает
(решение 45).
~~Идемпотентность загрузки — по имени файла, а не по содержимому.~~
Закрыто 2026-08-27 (soniks-network@b2f64840): дубликат разбирается по
SHA-256 принятого тела. Те же байты — 200 без записи в хранилище, другие —
перезапись оборвавшейся загрузки. Поля Observation.payload_sha256,
Observation.waterfall_sha256, DemodData.sha256.
Клиент в этом не участвует и участвовать не должен: хеш считает портал
(_artifact_sha256 в network/api/views.py), станции знать о нём незачем.
Пустой хеш означает строку, записанную до миграции 0035 — там сохраняется
прежнее поведение: разбор по имени файла и 403 с подстрокой has already been uploaded, на которой стоит api.py во всём парке. Опечатку «Watefall»
исправили в Фазе 0.20, подстроку — сохранили дословно.
~~Timestamp разбирается из имени файла demoddata через split("_")[2] без
обработки исключений — кривое имя даёт 500.~~ Закрыто 2026-08-24 (Фаза
0.19): разбор в _frame_datetime(), контракт имени — в одной функции
demoddata_path(), кривое имя даёт 400 с кодом malformed_filename.
Кадр вне окна наблюдения портал отвергает — с 2026-08-28. Метка из имени
кадра обязана попадать в [start − T, end + T], где T —
DEMODDATA_TIME_TOLERANCE_MINUTES (5 минут по умолчанию); иначе 400 с
кодом frame_outside_window. Допуск — на уход часов станции, не на кадр из
другого прохода. Метку ставит диспетчер по часам станции, поэтому станция с
часами, ушедшими дальше пяти минут, получит 400 на каждый кадр.
С 2026-09-10 клиент считает любой 400 окончательным (FileRejectedError),
по той же схеме, что 404: первый отказ — файл в incomplete/, повторный —
файл закрывается как выгруженный (решение 24 дорожной карты). Уход часов
станции виден в /healthz полем clock_skew_seconds — клиент сверяет часы с
заголовком Date ответа /api/jobs/ (решение 25).
~~«Конфигурация станции» на странице станции — бутафория.~~ Закрыто
2026-09-10: четыре поля satnogs_* удалены, вместо них
Station.desired_config (плоский словарь, тот же словарь имён, что
MANAGED_FIELDS клиента и STATION_CONFIG_FIELDS портала —
network/base/station_config.py), config_generation, форма
stations/<id>/config/. Заготовки get_default_station_configuration*
остаются: на них ссылается миграция 0005.
Список наблюдений ограничен по частоте — с 2026-08-29. GET /api/observations/ отдаёт 60 запросов в час анониму и 240 с токеном
(OBSERVATIONS_LIST_*_THROTTLE_RATE); карточка, планирование и выгрузка
артефактов не ограничены, станция лимита не видит. Видит его
tools/portal_stats.py: он листает список постранично до PAGE_LIMIT (200)
страниц, и два прогона в час подряд упрутся в 429. Запускать с токеном и не
чаще раза в час на большой выборке.
Контракт клиент ↔ диспетчер¶
src/soniks_client/flowgraph.py строит командную строку механически: --{key}={value}
из словаря self.parameters, None не передаётся. Имя поля настроек и имя
аргумента связаны вручную (RX_SAMP_RATE → --samp-rate-rx).
Диспетчер (flowgraph_dispatcher.py) вызывает parse_args(), не
parse_known_args(). Значит любой аргумент, которого он не знает, — это выход с
кодом 2 до запуска графа и подписчиков. Popen при этом успешен, клиент
через секунду видит мёртвый процесс и штатно закрывает наблюдение.
Симптом рассинхронизации версий: наблюдение «успешно», данных нет вообще — ни кадров, ни аудио, ни водопада.
Состояние контракта, проверено на стенде 2026-08-24¶
Дифф «что клиент шлёт» против flowgraph_dispatcher --help:
Аргумент |
Состояние |
|---|---|
|
клиент шлёт, диспетчер не знает. Не стреляет: портал не отдаёт |
|
клиент шлёт, диспетчер не знает. Не стреляет: |
|
до 2026-09-12 принимался, форвардился и терялся: |
|
диспетчер принимает, клиент не шлёт никогда |
остальные 27 |
совпадают |
Портал отдаёт norad_cat_id только по опт-ину (с 2026-08-27). Без
параметра capabilities в ответе /api/jobs/ тех же одиннадцать полей, что и
раньше: id, start, end, ground_station, tle0..2, frequency, mode,
transmitter, baud — и JobData.from_dict получает через .get() свой
None. С capabilities=norad_cat_id добавляется двенадцатое поле.
С 2026-09-07 опт-ин — список из трёх имён (JobSerializer.OPT_IN_FIELDS):
norad_cat_id, transmitter_parameters (снимок Transmitter.params на
момент планирования — материал Фазы 3.2, выбор декодера по параметрам
передатчика, а не по одному mode) и max_altitude (максимальная высота
прохода). Клиент объявляет только norad_cat_id; остальные два появятся в
CAPABILITIES, когда у них будет потребитель в клиенте, не раньше.
В /api/observations/<id>/ поле norad_cat_id было всегда — данных порталу
хватало и раньше, не хватало поля в JobSerializer. Заодно проверено: этот
эндпоинт открыт анонимно и возвращает
client_metadata строкой, внутри которой signal.snr_db — тоже строка вида
"13.8 dB" (так форматирует Waterfall.get_signal_metadata()). На этом стоит
проверка SNR в вердикте стенда.
--norad-cat-id в клиенте — задел, а не оплошность: gr-satellites выбирает
декодер по NORAD через satyaml, и без этого аргумента функционал не включить.
Все три звена контракта готовы: клиент шлёт, диспетчер принимает (в исходниках
soniks-flowgraphs, до станции доезжает только пересобранным образом),
JobSerializer отдаёт по опт-ину.
Практический вывод: оба неизвестных аргумента латентны, сеть не пишет
пустоту на каждом проходе. Но безусловное добавление norad_cat_id в
JobSerializer глушит парк одномоментно и без единой ошибки в логе.
Диспетчер научился 2026-08-24: parse_known_args() плюс явные
--norad-cat-id и --lo-transverter, которые принимаются и обнуляются. Правка
написана заново — origin/soniks-update мёржу не подлежит (решение 19 в
дорожной карте).
Предупреждение
До 2026-08-27 отсюда следовало: «предусловие для портала снимает не коммит, а
пересобранный образ». Утверждение снято решением 21 дорожной карты: парк не
обновится целиком никогда, значит выкатка гейтом быть не может. Опасная
комбинация — клиент шлёт --norad-cat-id, диспетчер того же образа его не
знает — стоит на станциях уже сейчас: аргумент появился в клиенте в 2.2.0
(1 июля), образ парк получил 19 июля. Станции на 1.x аргумент не шлют и
безопасны.
Гейт вместо выкатки — опт-ин: клиент объявляет capabilities в
GET /api/jobs/ (CAPABILITIES в src/soniks_client/api.py), портал отдаёт
поле только попросившему. Обе половины сделаны 2026-08-27:
JobSerializer.to_representation() убирает norad_cat_id из ответа тому, кто
возможность не объявил, JobCapabilitiesTest сторожит неизменность ответа для
всех остальных.
Три вещи, которые видно только из портала и о которых стоит помнить:
ObservationViewFilter— обычныйFilterSetбез строгости, django-filter неизвестные query-параметры игнорирует. Поэтому клиент мог слатьcapabilitiesв портал, который о нём не знал, без единого последствия. Введение строгости сломало бы именно новых клиентов — на это стоит тест;JobView.listдо 2026-08-27 строилJobSerializer(...)напрямую, без контекста. Любая логика сериализатора, зависящая отrequest, там молча не работала. Теперьself.get_serializer(...), как вretrieve;Observation.satellite—null=True. Всё, что читаетobj.satellite.*в сериализаторах расписания, обязано это переживать: исключение там роняет запрос расписания целиком, а не одно задание.
Проверять этот класс отказов надо на живом железе: код возврата диспетчера
клиентом не проверяется, упавший граф выглядит как досрочно закончившееся
наблюдение. Инструмент — tools/stand.sh deploy, печатающий этот дифф после
каждой выкатки; см. Стенд.
Два декодера на один сигнал¶
Неочевидная вещь, которую видно только при чтении обоих репозиториев сразу.
flowgraph_dispatcher→satnogs_*.py→ ZMQ → файл на кадр. Реалтайм, выбор декодера поmodeот портала. Кадры подхватывает watchdog и шлёт пачками ещё до конца прохода.gr_satellites, запускаемый изsatnogs-pre(scripts/grsat.py). Питается UDP-потоком IQ от того же графа, выбор декодера по NORAD ID через satyaml. С 2026-09-12 (Фаза 3.2) запускается только если клиент нашёл satyaml для NORAD: решение —sat_data.decoder_for(), едет в скрипт переменнойSONIKS_DECODERи вclient_metadataключомradio.decoder. ~~Кадры копятся в KISS и разбираются только наstop— то есть уже после остановки watchdog, и уезжают лишь заданием выгрузки после прохода. Не реалтайм.~~ Реалтайм с 2026-09-10 (Фаза 3.1):grsat.py streamчитает--kiss_serverи пишет кадр файлом сразу, watchdog шлёт его пачкой; разбор--kiss_outнаstopостался страховкой, дубли портал отсеивает по sha256.
С 2026-09-12 (Фаза 3.5) satyaml значит больше, чем выбор декодера: если режим
портала клиенту неизвестен, а satyaml для NORAD есть, проход не отказывается —
satnogs-граф берётся по модуляции первого передатчика (AFSK/BPSK/FSK),
потому что gr-satellites от него нужен только IQ; исходный режим уезжает в
radio.mode_requested. Станция публикует список NORAD из satyaml ключом
satellites в status/; портал с 2026-09-12 принимает его и пропускает такой
спутник при любом режиме — во всех путях планирования и в SQL-фильтре запусков
(решение 74). Bundle с 2026-09-12 подтягивается и между
проходами (resync_sat_data), а не только при старте контейнера.
Из двадцати флоуграфов блоки gr-satellites используют два: geoscan_gfsk_decoder
и usp_fsk_decoder. Дефреймеры gr-satellites не отдают метаданные PMT, из-за
чего в форке правился zmq_frame_decoder_sub.py.
Таблицы SCRIPTS и MODES в src/core/configs/flowgraph.py — копия,
клиент читает из них только script_filename и только чтобы передать имя в
pre/post-скрипты. На выбор флоуграфа они не влияют: его делает диспетчер по своей
таблице.
Предупреждение
Из этого раньше делался вывод «расхождения между копиями безвредны». Он неверен,
и на нём два обхода продержался баг SSTV_PD120. Потребитель у имени есть:
scripts/find_samp_rate.py определяет по подстрокам в нём частоту дискретизации
IQ — и для UDP-потока в gr-satellites (grsat.py), и для имени IQ-дампа
(iq_dump_rename.sh). Имя из чужого режима даёт неверную частоту обоим, без
единой строки в логе. Расхождение таблиц безвредно только для выбора графа.
Станция и внешняя сеть¶
Часть парка стоит в закрытых сетях, где доступен только sonik.space.
Сегодня станция обращается наружу в двух местах, и оба — за образами:
Куда |
За чем |
Когда |
|---|---|---|
|
rigctld, rotctld |
при |
|
клиент |
при |
~~Ещё два обращения — клоны satyaml из GitHub и GitLab при каждом старте
контейнера.~~ Закрыто 2026-08-28. scripts/liveupdate-satyaml.sh больше не
клонирует ничего: satyaml и sat.cfg приезжают sat-data bundle’ом с портала
(GET /api/v2/bundles/latest/, модуль src/soniks_client/sat_data.py),
кэшируются на постоянном томе и сверяются по sha256. Скрипт остался тонкой
точкой входа под прежним именем — на нём стоит command: в compose всего
парка.
Три вещи по этому пути, которые стоит помнить:
старт не зависит от портала. Портал лежит, архив битый, хеш не сошёлся — предупреждение в лог и работа на кэше либо на данных из образа;
применение отделено от скачивания. Кэш переживает перезапуск, а каталог satyaml внутри пакета
satellites— нет: он в эфемерной ФС контейнера. Поэтому кэш раскладывается на каждом старте, а не только при новой версии;версия видна постфактум:
client_metadata.radio.sat_dataрядом сflowgraphsиexit_code.Noneозначает станцию на данных из образа.
Практическое следствие для любой задачи об обновлении: пока образы лежат в публичных registry, станции в закрытых сетях недостижимы кодом — включая исправления критических багов. Данными они достижимы уже сейчас: новый спутник доезжает bundle’ом без пересборки образа.
Ограничение формы registry: Docker-клиент всегда ходит в https://<host>/v2/....
Повесить registry на префикс пути нельзя, поддомен в тех же сетях скорее всего
тоже закрыт. Значит /v2/ в корне sonik.space и имена вида
~~sonik.space/soniks-client:2.3.0~~ sonik.space/sonikspace/soniks-client:2.3.0:
registry — pull-through cache Docker Hub (решение 34 дорожной карты), путь
отображается на Docker Hub один-в-один, push в него нет, digest совпадает.
Сервис — профиль registry в compose портала, nginx пропускает только
sonikspace/* и librespace/hamlib. На 2026-09-10 на серверах не включён, и
шаблоны compose станции смотрят на Docker Hub до подтверждённого pull
(решение 37).
soniks-monitor — материал, а не основа агента¶
Не просто дашборд: в нём есть куски станционного агента — идентичность (STATION__ID + STATION__TOKEN), постоянная
связь с сетью, доступ к логам контейнеров (то есть docker.sock), системная
телеметрия, знание о текущем наблюдении. Он читает водопад прямо из
/tmp/.soniks/output, то есть делит spool с клиентом, и сам опрашивает API раз в
30 секунд, дублируя поллинг клиента.
Предупреждение
Путь /tmp/.soniks/output монитор считает константой, а он больше не таков:
пункт 9 Фазы 0 задал в обоих compose клиента PATHS__BASE=/var/lib/soniks-client/data,
чтобы очередь incomplete/ переживала перезапуск. Монитор, поднятый рядом с
обновлённым клиентом, водопадов не найдёт и не скажет об этом — путь у него
захардкожен. Чинить на стороне монитора, когда до него дойдёт очередь.
Ходит по WebSocket (WS_URL, LOG_POST_URL) — эндпоинтов под это в
soniks-network нет.
~~Принятое решение: монитор и OTA-агент — один демон.~~ Смягчено решением
30: агент пишется с нуля, монитор — материал Фазы 5 (логи через
docker.sock, разбор водопада). Основой он служить не может: из Docker он
только читает логи, секрет станции уходит в URL и в лог, данные — на сторонний
сервер. Контракт агента v1 — решения 38–44 в Дорожная карта: надёжность сети.
Ловушки, видимые только межрепозиторно¶
Имя .deb флоуграфов — часть контракта, и оно уже разъезжалось. Dockerfile
клиента собирает soniks-flowgraphs в пакет и ставит его по маске из
родительского каталога. Имя пакета задаётся debian/control в соседнем
репозитории, и 2026-08-22 он был переименован из satnogs-flowgraphs в
soniks-flowgraphs (soniks-flowgraphs@248e606). Клиентская маска осталась
старой, glob перестал раскрываться, apt упал с Unsupported file — то есть
образ не собирался вовсе с 22 августа. Найдено только сборкой на стенде
2026-08-24: ни один тест и ни один линтер этого не видит, а джоба docker в CI
идёт только по тегу, поэтому пайплайны оставались зелёными.
Практический вывод: любая правка debian/control во флоуграфах — это правка
Dockerfile клиента. Проверяется единственным способом — реальной сборкой.
С 2026-08-24 сборка есть в CI и без тега (джоба docker_build_check: по
расписанию автоматически, на ветках вручную), а FLOWGRAPHS_BRANCH пиннут
тегом — то есть имя .deb теперь может разъехаться только вместе с осознанным
подъёмом пина.
Версия станции определена наполовину (было — «не определена»). Закрыто
2026-08-24: gr-satnogs берётся из форка gr-soniks по тегу v3.1.0.1,
flowgraphs — по тегу 2.6.0, оба коммита записываются в образ и уезжают
порталу в client_metadata.radio (version и flowgraphs). Версию клиента
задаёт тег GitLab и только он: в репозитории лежат заглушки 0.0.0, а
CLIENT_VERSION подставляет CI из $CI_COMMIT_TAG.
Остаётся незакрытым: плавающий тег latest-addons, который тянут оба
docker-compose.yml. Satyaml из git при старте контейнера закрыт 2026-08-28
bundle’ом (см. выше): определения теперь версионированы и их версия уезжает
порталу. То есть по
собранному образу код теперь восстановим точно, а вот какой именно образ
стоит на конкретной станции — по-прежнему нет. Это Фаза 2 (агент и
desired-state), не Фаза 1.
Поставка образов ручная, и из кода этого не видно. Джоба docker в
.gitlab-ci.yml каналом поставки никогда не была: в registry лежат
однопланформенные arm64-образы, ни одного manifest list, а версионных тегов
образов нет вовсе — парк живёт на latest-addons. Собирает и заливает человек
командой ./build.sh <версия> --push; мультиарх под QEMU в CI — незаконченная
работа, падающая на раннере. С 2026-08-27 образ расслоён на soniks-base
(гигабайты, пиннутый неизменяемый тег) и клиентский слой (десятки мегабайт), а
build.sh отказывается публиковать без версии: образ с 0.0.0 обнуляет
разбивку наблюдений по версиям клиента и оставляет парк без точки отката.
Подробности — «Выкатка образа» в Участие в разработке.
Практическое следствие для правок Dockerfile: *_BRANCH — теги, а не ветки.
Чтобы в образ попал новый код соседнего репозитория, там нужен новый тег;
правка в ветке до образа не доезжает вовсе.
Водопады разных режимов несопоставимы. Полоса равна out_samp_rate, а он
зависит от бодрейта: 38.4 кГц для 9600, 48 кГц для FM, 66.56 кГц для APT и
SSTV_PD120. Любое сравнение noise_db между режимами некорректно — это блокирует
алгоритмы автоподбора усиления.
Аппаратная AGC выключена во всех графах намеренно. Для узкополосных пакетных сигналов она подстраивается под сильнейший сигнал в полосе, обычно помеху. Не включать «чтобы улучшить приём».
Двадцатикратная копипаста в .grc. Блок soapy_source с ~45 полями
(включая мёртвый канал 1) и доплер-сниппет продублированы построчно. Любая правка
обработки усиления или адреса rigctld — это двадцать одинаковых изменений.
Каталог hierarchical/ с шестью блоками задуман как лекарство и не используется
ни одним графом.
Ничего не проверяется по-настоящему без SDR. ~~Тестов нет на
execute_observation, main.Application, api.py,
communication_session.py (360 строк конкурентного кода),
waterfall/deviation.py (519 строк DSP).~~ Закрыты 2026-08-26/27
(tests/test_execute_observation.py, test_application.py,
test_api_transport.py, antenna/test_communication_session.py) и
2026-09-12 (tests/test_deviation.py — оценщик на синтетических
спектрограммах, 93 %); порог покрытия в CI — 80%.
Реальная проверка по-прежнему — прогон на стенде либо
scripts/test-flowgraph.sh, но только по команде человека (решение 23
дорожной карты): без команды правка закрывается тестами репозитория, и работа
идёт дальше. В soniks-flowgraphs tests/e2e_check.py
подключён к CI с 2026-08-24.
Где искать дальше¶
Вопрос |
Где |
|---|---|
План работ, принятые решения, контракты |
|
Что уже закрыто из техдолга и что было отклонено осознанно |
|
Устройство клиента, жизненный цикл прохода |
|
Режимы демодуляции, как добавить режим |
|
Судьба файлов по итогу выгрузки |
|
Формат |
|
Устройство флоуграфов и диспетчера |
|
Прежде чем предлагать вернуться к чему-то, загляните в Дорожная карта: закрытие техдолга: часть очевидных на вид правок там уже рассмотрена и отклонена с обоснованием — в частности процесс-wide лок на наложившиеся проходы. Не переигрывайте такие решения молча.
Снимок¶
Состояние, на котором проверялись все факты выше.
Репозиторий |
Ветка |
Коммит |
Дата проверки |
|---|---|---|---|
|
|
|
2026-09-10 |
|
|
|
2026-09-12: золотой фронтенд, |
|
|
|
2026-09-10, без изменений |
|
|
|
2026-09-12: |
|
— |
без git |
2026-09-11, создан |
|
|
|
2026-09-10 |
|
|
|
2026-09-10, без изменений |
soniks-satyaml появился в таблице вместе с sat-data bundle: теперь это не
только источник определений, но и сборщик архива, который едет на станции,
и в нём же лежит sat.cfg. 3b6d763 — коммит, который добавил CI и перенёс
sat.cfg; сабмодуль в клиенте на него переставлен.
Портал за 28 августа — 10 сентября ушёл на 115 коммитов (52a4abd2 →
9ece8db4), и большинство утверждений о нём выше пришлось перепроверить.
Что из этого касается клиента:
платформа (2026-09-02,
cba0d33e): Python 3.12, Django 5.2 LTS, DRF 3.18, Redis 7.4, bookworm. Образ собирается в CI и выкатывается из registry GitLab, а не собирается на сервере;правило 21 закреплено и расширено: инвариант 7 в его
docs/ai/context.md,urls_v2.pyдля всего нового, три опт-ин поля в/api/jobs/(b903cc93, 2026-09-07), аналитика под/api/v2/с алиасами. Удалённое из моделиvetted_statusпродолжает отдаваться вычисленным;строже к присланному:
400 frame_outside_windowна кадр вне окна (25f25da7, 2026-08-28), троттлинг списка наблюдений (f379b938);модель станции (2026-09-06,
6c42f5e7): состояние не хранится, четвёртое состояниеunavailable, в v1 отдаётся какOffline;снимок передатчика на наблюдении (2026-08-28): оценка и опт-ин
transmitter_parametersчитают его, а не справочник;доступ (
79babbb9, 2026-08-29): ротация токена только поPOST, анонимныйPOST /api/observations/отвечает 401, а не 400;не изменилось:
has already been uploadedдословно,/api/v2/bundles/, content-hash,JOBS_ALLOW_ANONYMOUS,OBS_AUDIO_DURATION_TOLERANCE,ClientIDAuthenticationне подключён, бутафорские поля конфигурации на месте, registry наsonik.spaceнет,clean_observationsудалена без замены.
Остальное — фронт (Bootstrap 5.3, снятие jQuery, дизайн-система), TLE и автопланирование, страницы спутников и запусков — станции не касается.
10–11 сентября портал ушёл ещё на 8 коммитов (8dd678e4 → 6158eaf7):
настройки станции и state/, MQTT-брокер и ReleaseChannel (9b58f1f7),
команды станции и обследование приёмника (96806c28), registry-прокси
(7f922b5e); из остального станции касается только 282eb9cb, где
close_old_connections() в мосте MQTT перенесён из handle_message в
on_message — иначе мост ронял соединение с БД внутри транзакции. Глобус,
фильтры списков и модалка спутника — фронт. Правки этой сессии (ключ
agent, валидатор digest, миграция 0057) на 2026-09-11 не закоммичены.
Если соседний репозиторий ушёл вперёд — перепроверьте затронутые утверждения и обновите эту страницу вместе с правкой.