Критичный баг-фикс по режиму «Пожелания из совещания». Проблема: сервер Devprom на POST /issue/items молча игнорирует поле Content, упомянутое в публичной документации /docs/8835.html. Запись создаётся с пустым телом, HTTP 200, UID возвращается — никакого сигнала об ошибке. Правильное имя поля для тела пожелания на эндпоинте /issue/items — Description. Выявлено при проверке U-6186 (Артур): поле Description пустое, хотя POST вернул валидный UID. Та же проблема обнаружилась на 6 пожеланиях Тепловин U-6189..U-6194 — все с пустым телом. Пересоздано как U-6198..U-6203 с полем Description, размер тела 1565–2172 символа, верификация пройдена. Правки скилла: - devprom-alm-api.md §0: новый блок «Критично: сервер молча отбрасывает незнакомые поля» + обязательная схема контрольного GET с ассертами. Список известных тихих ловушек (Content вместо Description, Type.Id='' в request/items, Requirement в POST). - devprom-alm-api.md §2: рецепт с Description + встроенные ассерты. - devprom-alm-api.md §3: справочник полей — Description с пояснением про Content и документацию, требование GET-проверки. - devprom-alm-api.md appendix: curl-примеры с Description. - meeting-wishes-extraction.md Шаг 6: Description в инструкции + ссылка на секцию «тихое отбрасывание». - meeting-wishes-extraction.md Шаг 7: жёсткая формулировка «Ответ POST — недостаточное подтверждение успеха», ассерты на префикс UID, содержимое Description, Priority, Function; визуальная проверка в UI. - meeting-wishes-extraction.md Python-шаблон: Description + ассерты на размер тела (>100 символов).
18 KiB
Извлечение Пожеланий из транскрипции совещания
Методика превращения транскрипции рабочего совещания с заказчиком в набор формализованных Пожеланий в Devprom ALM. Проверена на совещании с главбухом АРД (21.04.2026).
Связанные файлы:
references/devprom-alm-api.md— как писать в Devprom через API- Скилл
docx— для возможного экспорта сводки в Word
Критерии отбора: что становится Пожеланием, а что нет
Становится Пожеланием
- Доработка конфигурации — новый реквизит, новое правило, новая форма, интеграция. Пример: «Допреквизит „Доступно для менеджера" в справочнике номенклатуры».
- Изменение бизнес-процесса с цифровым следом — новая цепочка документов, автоматизация этапа, блокировка операции. Пример: «Запрет проведения отгрузки при отрицательном остатке».
- Типовая настройка с конкретным целевым эффектом — использование типового механизма там, где клиент раньше работал руками. Пример: «Использовать типовой механизм Номенклатура контрагентов».
- Интеграция с внешней системой — ЭДО, клиент-банк, маркетплейс, CRM, АТС. Пример: «Автоматическая отправка отгрузочных документов в Диадок».
НЕ становится Пожеланием
- Методика внутренней работы заказчика — как они сейчас живут в Excel, как главбух разбирает почту.
- Нерешённые бизнес-вопросы без автоматизационного следа — «надо решить, кто отвечает за ставку НДС», «КП оформляем в Word или в 1С».
- Оргмоменты и ответственность — назначение ролей, регламент согласований без запроса автоматизации.
- Общие рассуждения и отступления — истории про то, как раньше работали.
Проверочный вопрос
«Если бы это пожелание взял в работу разработчик конфигурации — что конкретно он бы сделал?» Если ответ неконкретный («поговорил с главбухом», «уточнил процесс», «подумал») — это не пожелание, а открытый вопрос в протокол совещания.
Пайплайн
Шаг 1 — Выделение тем и спикеров
Читаем транскрипцию целиком. Для каждой темы фиксируем:
- Тему (1 фраза)
- Инициатора (кто первым её подняла + таймкод)
- Подтверждающих (кто согласился, уточнил, добавил детали)
- Открытые вопросы (если обсуждение заблокировалось)
Нотация: [Спикер тайм-код] → ключевая мысль.
Шаг 2 — Кластеризация
Одна тема может обсуждаться несколько раз в разных частях совещания — в начале, в середине (уточнение), в конце («а ещё мы забыли...»). Все упоминания одной темы объединяем в одно Пожелание. В Источники блока перечисляем все таймкоды всех спикеров.
Шаг 3 — Отсев по критериям
Применяем проверочный вопрос из раздела «Критерии отбора». Оставшееся — кандидаты на Пожелания. То, что отсеяли, записываем отдельным списком в сопроводительную заметку («Для обсуждения на внутреннем совещании заказчика») — это протокол, а не Devprom.
Шаг 4 — Классификация
Для каждого Пожелания-кандидата:
- Caption — одна ёмкая фраза, суть. 10–15 слов. Можно цитировать термин конфигурации («допреквизит», «заказ покупателя»).
- Priority — по акцентам речи:
1(Критично) — заказчик повторил несколько раз, сказал «100%», «без этого работать нельзя», или речь про блокировку ошибок/убытков.2(Высокий) — инициатор явно настаивал, есть конкретная цель.3(Обычный) — обсудили, согласились, без нажима.
- Function — подсистема КА 2.5 / ERP 2.5. См.
references/ka25-capabilities.md. Типично: НСИ, Продажи, Закупки, Склад, Казначейство, Производство, Бухгалтерия, Управленческий учёт. - Предметная область — уточнение внутри подсистемы («Управление доступом менеджеров к НСИ», «Контроль остатков»).
Шаг 5 — Формирование Description
Единый шаблон HTML для поля Content в Devprom:
<p><b>Заказчик:</b> ФИО, должность (email)</p>
<p><b>Описание:</b></p>
<p>Полное описание пожелания в свободной форме — что должно делать,
какие условия, какие границы. 3–7 предложений.</p>
<p><b>Источники:</b></p>
<ul>
<li>ФИО спикер-1, должность — роль в обсуждении (тайм-коды)</li>
<li>ФИО спикер-2, роль — (тайм-коды)</li>
</ul>
<p><b>Цитаты:</b></p>
<blockquote>Спикер (тайм-код): «прямая цитата».</blockquote>
<blockquote>Спикер (тайм-код): «прямая цитата».</blockquote>
<p><b>Предметная область:</b> Подсистема / Конкретика</p>
<p><b>Примечание:</b> (опционально) — например, «функционал типовой,
требует настройки процесса, без разработки»</p>
<p><b>Источник:</b> Совещание от DD.MM.YYYY, файл
<code>transcript.txt</code></p>
Правила по содержимому:
- Цитаты — обязательны, 1–3 штуки на пожелание. Без цитат будущий разработчик не поймёт контекст.
- Цитаты прямые — без редактуры речи. Сохранять разговорный стиль, паузы, просторечия. Это контекст, а не литература.
- Описание в повествовательной форме — не «пользователь хочет, чтобы», а прямое «Система должна…», «В справочнике номенклатуры ввести…». Подлежащим делаем объект системы, не пользователя.
- Никаких оценок объёма и сроков — это предмет gap-анализа, не этапа извлечения пожеланий.
- Никаких архитектурных решений — если пожелание порождает архитектурные вопросы, они идут отдельным Пожеланием с типом «Архитектурное решение» или в протокол совещания.
Шаг 6 — Загрузка через API
Один POST на одно Пожелание. Никакой привязки к требованиям,
никаких PUT. Поля Requirement и RequirementDocument остаются
пустыми — пользователь вручную прикрепит их в UI, если/когда
появится профильное требование.
Тело пожелания отправляется в поле Description, НЕ Content.
Публичная документация Devprom (/docs/8835.html) упоминает Content,
но на эндпоинте /issue/items это поле молча игнорируется сервером
при POST. Эмпирически проверено — см. references/devprom-alm-api.md,
раздел 0 «Сервер молча отбрасывает незнакомые поля».
post_body = {
"Caption": wish["caption"],
"Description": CUSTOMER_NOTE + wish["body"] + SOURCE_NOTE, # ← Description!
"Priority": {"Id": wish["priority"]}, # "1"/"2"/"3"
"Author": {"Id": AUTHOR_ID},
"Function": {"Id": wish["function_id"]},
"Company": {"Id": COMPANY_ID}, # опционально
}
# POST /issue/items → {UID: "U-xxxx", ...}
Подробный справочник полей и поведение API — в
references/devprom-alm-api.md.
Шаг 7 — Верификация по факту (обязательно)
Ответ POST — недостаточное подтверждение успеха. Сервер Devprom
возвращает HTTP 200 и валидный JSON с UID даже тогда, когда часть
полей была молча отброшена (например, из-за неправильного имени).
Верификация идёт по результату GET, а не по ответу POST.
Для каждого созданного пожелания — контрольный GET и ассерты:
code, text = call("GET", f"issue/items/{rid}")
g = json.loads(text)
assert g["UID"].startswith("U-"), f"неверный префикс: {g['UID']}"
assert len(g.get("Description","")) > 100, "тело пожелания пустое"
assert (g.get("Priority") or {}).get("Id") == expected_priority
assert (g.get("Function") or {}).get("Id") == expected_function_id
Дополнительно — после загрузки всей пачки открыть UI-раздел
/pm/<project>/module/requirements/issues, убедиться визуально:
- Все загруженные пожелания отображаются одним списком
- UID у всех — префикс
U- - Случайно выбранная карточка открывается, форматирование HTML работает,
ссылки (mailto:) кликабельны, цитаты в
<blockquote>отрисованы - Поля
RequirementиRequirementDocumentпустые — это норма, привязку делает пользователь вручную в UI
Шаг 8 — Сводка для пользователя
После загрузки отдать:
- Таблица «№ / UID / Функция / Приоритет / Название / Ссылка»
- Список того, что намеренно НЕ стало Пожеланиями — короткие формулировки с причиной («методика», «открытый вопрос», «оргмомент»).
Скрипт-шаблон (Python, без внешних зависимостей)
Шаблон есть в templates/wishes-upload.py (при наличии). Ключевые моменты:
import json, urllib.request, urllib.error, ssl, time
HDR = {"Devprom-Auth-Key": "<KEY>",
"Content-Type": "application/json",
"Accept": "application/json"}
BASE = "https://<host>/pm/<project>/api/latest"
def call(method, path, body=None, retries=3):
"""urllib + ретраи по 502/503/504 от egress-прокси."""
data = json.dumps(body, ensure_ascii=False).encode("utf-8") if body else None
for attempt in range(1, retries+1):
req = urllib.request.Request(f"{BASE}/{path}", data=data,
headers=HDR, method=method)
try:
with urllib.request.urlopen(req, timeout=25,
context=ssl.create_default_context()) as r:
return r.status, r.read().decode("utf-8")
except urllib.error.HTTPError as e:
if e.code in (502, 503, 504) and attempt < retries:
time.sleep(3 * attempt); continue
return e.code, e.read().decode("utf-8")
for wish in WISHES:
post_body = {
"Caption": wish["caption"],
"Description": CUSTOMER_NOTE + wish["description"] + SOURCE_NOTE, # ← Description
"Priority": {"Id": wish["priority"]},
"Author": {"Id": AUTHOR_ID},
"Function": {"Id": wish["function"]},
"Company": {"Id": COMPANY_ID}, # опционально
}
code, text = call("POST", "issue/items", post_body)
r = json.loads(text); r = r[0] if isinstance(r, list) else r
issue_id = r["Id"]
# ⚠ ОБЯЗАТЕЛЬНАЯ верификация: сервер молча отбрасывает незнакомые поля.
# Ответа POST недостаточно — смотрим, что реально сохранилось.
_, t = call("GET", f"issue/items/{issue_id}")
g = json.loads(t)
assert g["UID"].startswith("U-"), f"префикс не U-: {g['UID']}"
assert len(g.get("Description","")) > 100, f"пустое тело: Id={issue_id}"
Анти-паттерны
- Создание через
/request/items— даст префикс I- и попадание в «Заявки». Использовать только/issue/items. - Создание документов требований, Company, Feature или IssueAuthor
из этого режима —
POST /requirement/items,POST /company/itemsи т.п. в режиме извлечения Пожеланий не вызывать. Эти справочники готовит владелец проекта вручную через UI Devprom заранее. Если нужных записей нет (например, нет подходящей Function) — остановиться, запросить их создание, и только после этого продолжать загрузку. - Привязка Пожелания к документу требований при создании — поля
RequirementиRequirementDocumentв теле POST не передавать. Пользователь сам прикрепит пожелание к нужному требованию в UI, когда это требование появится. Попытка «сразу правильно» привязать ломает процесс согласования с заказчиком. - Цитаты в пересказе — обезличивают пожелание, делают его неотличимым от прочих. Только прямые цитаты в blockquote.
- Смешение в одном Пожелании двух разных требований — потом невозможно оценить объём и трассировать. Один атомарный запрос — одно Пожелание.
- Создание IssueAuthor через API — заблокировано на уровне прав. Только через UI.
- Попытка удалить запись через
DELETE /request/items/{id}— сервер отвечает 200 +Unable access entity. Удалять черезDELETE /issue/items/{id}— он работает для любой записи, в том числе для I- (созданных через/request/items).
Memo для будущих расширений
Потенциальные улучшения, если появится потребность:
- Автопривязка транскрипции к пожеланию через
/attachment/items(прикрепление.txtфайла с полной транскрипцией к одному «главному» Пожеланию или к документу требований). - Экспорт сводки в Word через скилл
docx— для отправки клиенту или приобщения к протоколу. - Автоопределение Priority по акцентам — через LLM-классификацию фраз типа «100%», «критично», «можем обсудить позже».
- Автоопределение Function — словарь ключевых слов → подсистема (см.
references/ka25-capabilities.md).