lct-01: контракт закрыт — координаты, обязательные поля, коды ошибок

Координаты были представлены дважды: кортежем в КИО и объектом в данных
ЭРА-ГЛОНАСС. Теперь Coords {lat, lon} везде — в кортеже не видно, где
широта, и ошибка всплывает на карте у диспетчера, а не в типах.

required_fields едет в call.incoming и session.snapshot: обязательность
полей задаёт сценарий, без списка АРМ не подсветит незаполненное поле,
и курсант узнаёт о неполноте карточки только из разбора.

Канал error получил коды (ErrorKind, восемь значений) — фронт разбирает
код, а не текст сообщения. Имя не ErrorCode: оно занято таксономией E1–E6,
два одинаковых имени дали бы коллизию в generated.ts.

Добавлены поля, которые спрашивает чек-лист, а записать было некуда:
куда идёт дым, уехал ли нарушитель, код домофона.

Документы догнали код: mkh → gkh, payload transcript.append с якорем ref,
причина director у tts.cancel, attempt и stopped у таймеров.
This commit is contained in:
Ivan Gerasimov 2026-09-15 19:48:56 +03:00
commit 59d5a6a26d
5 changed files with 106 additions and 17 deletions

View file

@ -25,6 +25,11 @@ HEADER = """// Сгенерировано `make types` из backend/app/domain/e
"""
def oneline(text: str) -> str:
"""Docstring в несколько строк — комментарий в TS в одну."""
return " ".join(text.split())
def pascal(name: str) -> str:
return "".join(part.capitalize() for part in name.split("_"))
@ -90,7 +95,7 @@ def fields(node: dict[str, Any], indent: str = " ") -> list[str]:
optional = "" if name in required or "const" in prop else "?"
description = prop.get("description")
if description:
lines.append(f"{indent}/** {description} */")
lines.append(f"{indent}/** {oneline(description)} */")
lines.append(f"{indent}{name}{optional}: {ts_type(prop)};")
return lines
@ -100,7 +105,7 @@ def inline_object(node: dict[str, Any]) -> str:
def render_def(name: str, node: dict[str, Any]) -> str:
doc = node.get("description", "").strip()
doc = oneline(node.get("description", ""))
comment = f"/** {doc} */\n" if doc else ""
if "enum" in node and "properties" not in node:
values = " | ".join(literal(value) for value in node["enum"])