feat: билеты 1–3 заказчика в библиотеку сценариев (lct-34, частично)

Девять вызовов из билетов учебного центра в scenarios/tickets/. Библиотека
стала вложенной: загрузчик ходит по подкаталогам, checklists/ и pregenerated/
сценариями не считаются. Поля ticket и position задаются вместе.

Написаны чек-листы, которых не было: medical, police, utility. Без них
переносить было не на что — в библиотеке существовал только пожарный.

Девять вызовов закрывают все приёмы, ради которых билеты и брались: адрес
по ориентиру, адрес из другого региона, линейный объект (съезд с МКАД),
непрофильный вызов, профильный но не экстренный, массовая драка, где список
оповещения поднимает и полицию, и скорую.

Подводный камень записан в SCENARIO-FORMAT: значения признаков ЕКП бывают
с запятыми («Человек в воде, на льдине»), и в короткой записи YAML разрывает
такое значение на два.

Осталось семь билетов — работа механическая, рецепт отработан на трёх.

161 тест зелёный.
This commit is contained in:
Ivan Gerasimov 2026-09-20 08:38:01 +03:00
commit 4c2bbfd021
7 changed files with 280 additions and 3 deletions

View file

@ -101,7 +101,7 @@ def test_refinement_happens_once(slots):
@pytest.fixture(scope="module")
def library_scenario():
return load_file(LIBRARY / "fire-private-house-l3.yaml", LIBRARY)
return load_file(LIBRARY / "tickets" / "t03-1-fire-private-house.yaml", LIBRARY)
def test_library_scenario_expects_the_refined_address(library_scenario):

View file

@ -0,0 +1,55 @@
# Билет 2, вызов 1 (docs/spec/TICKETS.md). Дым без открытого пламени: курсант
# должен различить задымление и пожар — в классификаторе это разные коды
# и разные списки оповещения.
id: t02-1-smoke-chute
title: "Задымление мусоропровода в жилом доме"
ticket: 2
position: 1
type: fire
level: L2
topics: [fires]
modes: [training, exam, self]
extends: checklists/fire.yaml
signs: [жилой дом, мусоропровод, дым]
persona:
base: worried
arc:
- { stage: registration, mood: worried }
- { stage: interview, mood: worried }
first_line: "Здравствуйте, у нас дымом из мусоропровода тянет, весь подъезд в дыму."
facts:
- id: f_address
value: "улица Берзарина, дом 21, корпус 1, подъезд 3, домофон 68"
reveal_on: { question: q_address }
- id: f_what_burns
value: "дымит мусоропровод, открытого пламени не видно"
reveal_on: { question: q_what_burns }
- id: f_people
value: "пострадавших нет, люди в квартирах"
reveal_on: { question: q_people }
- id: f_smoke
value: "дым идёт по подъезду, заявитель на 7-м этаже, в доме 17 этажей"
reveal_on: { question: q_smoke }
- id: f_caller
value: "Ким Олег Юрьевич, телефон 916-126-34-71"
reveal_on: { question: q_caller }
checklist:
- { id: q_address, fact: f_address }
- { id: q_what_burns, fact: f_what_burns }
- { id: q_people, fact: f_people }
- { id: q_smoke, fact: f_smoke }
- { id: q_caller, fact: f_caller }
required_fields: [address, entrance, incident_type, description]
ground_truth:
address: "улица Берзарина, дом 21, корпус 1"
victims: 0
tree:
pregenerated: false

View file

@ -0,0 +1,53 @@
# Билет 2, вызов 2 (docs/spec/TICKETS.md). Непрофильный вызов: заявителю нужна
# не экстренная служба, а объяснение, куда обращаться. Карточка здесь не
# заводится — правильное действие — справка (механика в lct-36).
id: t02-2-megafon-consultation
title: "Скандал с продавцом связи — непрофильный вызов"
ticket: 2
position: 2
type: other
level: L3
topics: [nonprofile]
modes: [training, exam, self]
extends: checklists/police.yaml
signs: [Справка консультация, 101 102 103 104 112 прочие службы, В части касающейся службы]
outcome: consultation
persona:
base: aggressive
arc:
- { stage: registration, mood: aggressive }
- { stage: interview, mood: aggressive }
first_line: "Алло! Меня в салоне связи обманули, разбирайтесь!"
facts:
- id: f_address
value: "Сущёвский Вал, дом 5, строение 1, салон «Мегафон»"
reveal_on: { question: q_address }
- id: f_what_happened
value: "поругался с продавцом, тот бросил трубку; угроз и рукоприкладства не было"
reveal_on: { question: q_what_happened }
- id: f_victims
value: "никто не пострадал, помощь не нужна"
reveal_on: { question: q_victims }
- id: f_caller
value: "телефон 916 896 3254"
reveal_on: { question: q_caller }
checklist:
- { id: q_address, fact: f_address }
- { id: q_what_happened, fact: f_what_happened }
- { id: q_victims, fact: f_victims }
- { id: q_caller, fact: f_caller }
# Карточка не заводится: обязательных полей нет.
required_fields: []
ground_truth:
address: "Сущёвский Вал, дом 5, строение 1"
victims: 0
tree:
pregenerated: false

View file

@ -0,0 +1,54 @@
# Билет 2, вызов 3 (docs/spec/TICKETS.md). Адрес — только ориентир на набережной:
# ни дома, ни улицы, и уточнять придётся по ориентирам.
id: t02-3-car-in-water
title: "Автомашина упала в воду"
ticket: 2
position: 3
type: other
level: L3
topics: [rescue, address]
modes: [training, exam, self]
extends: checklists/police.yaml
signs: ["Человек в воде, на льдине", "Падение автомашины в воду"]
persona:
base: panic
arc:
- { stage: registration, mood: panic }
- { stage: interview, mood: panic }
- { stage: dispatch, mood: worried }
first_line: "Машина в воду ушла! Прямо с набережной! Там человек внутри!"
facts:
- id: f_address
value: "набережная Яузы, в сторону области"
refined: "набережная Яузы, напротив Большого Нижнего пруда"
reveal_on: { question: q_address }
refine_on: q_address_check
- id: f_what_happened
value: "автомашина съехала с набережной и ушла под воду"
reveal_on: { question: q_what_happened }
- id: f_victims
value: "в машине был человек, наружу никто не выбрался"
reveal_on: { question: q_victims }
- id: f_caller
value: "Иванова Елена Сергеевна, телефон 916 896 3254"
reveal_on: { question: q_caller }
checklist:
- { id: q_address, fact: f_address }
- { id: q_address_check }
- { id: q_what_happened, fact: f_what_happened }
- { id: q_victims, fact: f_victims }
- { id: q_caller, fact: f_caller }
required_fields: [address, incident_type, description, victims_count]
ground_truth:
address: "набережная Яузы, напротив Большого Нижнего пруда"
victims: 1
tree:
pregenerated: false

View file

@ -1,8 +1,10 @@
# Билет 3, вызов 1 (docs/spec/TICKETS.md). Взят первым из библиотеки заказчика,
# Билет 3, вызов 1 (docs/spec/TICKETS.md). Был первым перенесённым вызовом —
# потому что показывает приём, которого не было в наших сценариях: названный
# адрес существует и в Москве, и в области, и правильный узнаётся только
# переспросом. Механика — tasks/lct-35-address-refinement.md
id: fire-private-house-l3
id: t03-1-fire-private-house
ticket: 3
position: 1
title: "Пожар в частном доме, адрес уточняется"
type: fire
level: L3

View file

@ -0,0 +1,55 @@
# Билет 3, вызов 2 (docs/spec/TICKETS.md). Профильный, но не экстренный вызов:
# карточка заводится, наряд не срывается с пожара. Курсант должен не раздуть
# происшествие и не отмахнуться от него.
id: t03-2-loud-music
title: "Громкая музыка во дворе"
ticket: 3
position: 2
type: police
level: L1
topics: [police]
modes: [training, exam, self]
extends: checklists/police.yaml
signs: ["Нарушение тишины - в неразрешенное время", "Шум, музыка, ремонт, сигнализация автомашины"]
persona:
base: calm
arc:
- { stage: registration, mood: calm }
- { stage: interview, mood: calm }
first_line: "Добрый вечер. Во дворе музыка на всю громкость, второй час уже."
facts:
- id: f_address
value: "бульвар Маршала Рокоссовского, дом 25, во дворе"
reveal_on: { question: q_address }
- id: f_what_happened
value: "во дворе громко играет музыка из машины, компания сидит рядом"
reveal_on: { question: q_what_happened }
- id: f_victims
value: "никто не пострадал, драки нет"
reveal_on: { question: q_victims }
- id: f_suspects
value: "человек пять молодых людей, оружия не видно"
reveal_on: { question: q_suspects }
- id: f_caller
value: "Соколов Иван Петрович, телефон 916 896 3254"
reveal_on: { question: q_caller }
checklist:
- { id: q_address, fact: f_address }
- { id: q_what_happened, fact: f_what_happened }
- { id: q_victims, fact: f_victims }
- { id: q_suspects, fact: f_suspects }
- { id: q_caller, fact: f_caller }
required_fields: [address, incident_type, description]
ground_truth:
address: "бульвар Маршала Рокоссовского, дом 25"
victims: 0
tree:
pregenerated: false

View file

@ -0,0 +1,58 @@
# Билет 3, вызов 3 (docs/spec/TICKETS.md). Водитель заблокирован в машине:
# признак «Блокированные» поднимает и спасателей, и скорую. Адрес — линейный
# объект, съезд с МКАД: улицы и дома нет вовсе.
id: t03-3-log-blocked-driver
title: "Бревно упало на машину, водитель заблокирован"
ticket: 3
position: 3
type: other
level: L3
topics: [rescue, address]
modes: [training, exam, self]
extends: checklists/police.yaml
signs: ["ДТП пострадавшие", "Блокированные"]
persona:
base: panic
arc:
- { stage: registration, mood: panic }
- { stage: interview, mood: worried }
first_line: "С грузовика бревно слетело, прямо на легковую! Водитель зажат, не вылезает!"
facts:
- id: f_address
value: "съезд с МКАД на Симферопольское шоссе в область"
refined: "съезд с МКАД внутренней стороны на Симферопольское шоссе, 100 метров не доезжая, обочина"
reveal_on: { question: q_address }
refine_on: q_address_check
- id: f_what_happened
value: "бревно упало с грузовика на легковую машину"
reveal_on: { question: q_what_happened }
- id: f_victims
value: "водитель заблокирован в машине, сам выйти не может"
reveal_on: { question: q_victims }
- id: f_vehicle
value: "ВАЗ красного цвета, номер не видно"
reveal_on: { question: q_vehicle }
- id: f_caller
value: "Иванова Елена Сергеевна, телефон 916 896 3254"
reveal_on: { question: q_caller }
checklist:
- { id: q_address, fact: f_address }
- { id: q_address_check }
- { id: q_what_happened, fact: f_what_happened }
- { id: q_victims, fact: f_victims }
- { id: q_vehicle, fact: f_vehicle }
- { id: q_caller, fact: f_caller }
required_fields: [address, incident_type, description, victims_count]
ground_truth:
address: "съезд с МКАД внутренней стороны на Симферопольское шоссе"
victims: 1
tree:
pregenerated: false