33e53ee897
A scenario is a JSON file under cmd/mavend/testdata/scenarios: a start instant, a script of canned model answers, and a list of steps at "HH:MM". Each step does one thing — say, audio, signal, arrive, tick, fault — and then asserts on what she said, what was sent, which ecosystem services were called, and what landed in the intake journal. Between those boundaries the real components run: the real router cascade (stage0, the LLM router over a scripted completer, the classifier underneath it), the real store, the real reactive handler, the real tick loop, and the same intake-decorated ipc.CoreAPI the daemon wires. What is faked is only what a test cannot have: the model, the microphone, the speaker, the delivery sink, and the ecosystem HTTP services. Time is a single fakeClock threaded into every reader — the handler, the intake publish stamp and tick(ctx, now) — so there is no time.Now() on the replay path and a scenario is reproducible. TestSimulatorIsDeterministic enforces that by replaying twice and diffing the transcripts byte for byte; advanceTo refuses a step that goes backwards. Two scenarios ship. morning_missed replays #284's own description: he appears at the desk, a feed item, a mail candidate and a relayed notification arrive through the morning, two ticks pass, and the assertions are as much about nothing being sent at him unprompted as about what she said. evening_degraded picks up the tier-2 pipeline case #288 deferred here — a golden WAV through the STT seam to a written fact — and then puts the ecosystem into 503 and checks that the proactive loop stays quiet and that intake keeps working without it. This is test-only code. Nothing in the production binaries changed, so the daemon behaves identically when no scenario is running. `make simulate` runs them verbose so the transcript is readable; `make test` runs them with everything else. Vikunja #284
97 lines
3.6 KiB
JSON
97 lines
3.6 KiB
JSON
{
|
|
"schema_version": 1,
|
|
"name": "morning_missed",
|
|
"description": "The scenario from Vikunja #284's description, replayed. He appears at 08:30, things arrive through the morning while he is at the desk, and at 08:50 he asks what he missed. The assertions are as much about what did NOT happen — nothing was sent at him unprompted — as about what she said.",
|
|
"start": "2026-08-01T08:30:00+03:00",
|
|
"praxis_attention": "[{\"id\":\"item_1\",\"title\":\"medicine not taken\",\"importance\":3.0,\"rule\":\"morning_medicine\"}]",
|
|
"script": [
|
|
{
|
|
"match": "выпил воды",
|
|
"route": "[{\"intent\":\"fact\",\"key\":\"water\",\"value\":\"выпил\"}]"
|
|
},
|
|
{
|
|
"match": "записала факт: water",
|
|
"reply": "{\"response\":\"Записала, что ты выпил воды.\",\"mood\":\"neutral\"}"
|
|
},
|
|
{
|
|
"match": "что я пропустил",
|
|
"route": "[{\"intent\":\"query\",\"text\":\"что я пропустил\"}]"
|
|
},
|
|
{
|
|
"match": "",
|
|
"route": "[{\"intent\":\"chat\",\"text\":\"привет\"}]",
|
|
"reply": "{\"response\":\"Я рада тебя слышать.\",\"mood\":\"happy\"}"
|
|
}
|
|
],
|
|
"steps": [
|
|
{
|
|
"at": "08:30",
|
|
"note": "he appears at the desk",
|
|
"signal": { "key": "desk_active", "value": "true", "source": "infer:hyprland" },
|
|
"expect_events": ["infer:hyprland"],
|
|
"expect_no_send": true
|
|
},
|
|
{
|
|
"at": "08:32",
|
|
"note": "a feed item arrives, published half an hour ago",
|
|
"arrive": {
|
|
"source": "rss:tech",
|
|
"as_of": "08:02",
|
|
"note": { "text": "Вышло ядро 6.19 [tech]\nкраткое содержание\nhttps://example.org/a" }
|
|
},
|
|
"expect_events": ["rss:tech"],
|
|
"expect_no_send": true
|
|
},
|
|
{
|
|
"at": "08:35",
|
|
"note": "the mail reader extracts a candidate — a candidate is never spoken",
|
|
"arrive": {
|
|
"source": "email:inbox",
|
|
"task": { "text": "продлить домен", "evidence": "Домен истекает через 7 дней" }
|
|
},
|
|
"expect_events": ["email:inbox", "продлить домен"],
|
|
"expect_no_send": true
|
|
},
|
|
{
|
|
"at": "08:40",
|
|
"note": "the work calendar signal — a relayed notification, below full confidence",
|
|
"arrive": {
|
|
"source": "ambient:notif",
|
|
"fact": {
|
|
"key": "calendar_event_20260801_планёрка",
|
|
"value": "10:00-11:00 планёрка"
|
|
}
|
|
},
|
|
"expect_events": ["ambient:notif", "планёрка"],
|
|
"expect_no_send": true
|
|
},
|
|
{
|
|
"at": "08:45",
|
|
"note": "a tick with him present and nothing wrong must stay silent",
|
|
"tick": true,
|
|
"expect_no_send": true
|
|
},
|
|
{
|
|
"at": "08:50",
|
|
"note": "he asks. The query path answers from local recall only: nothing stored clears the score gate, so she refuses rather than inventing a morning summary, and the replier is never reached. That refusal is the no-hallucination floor and this step pins it.",
|
|
"say": "что я пропустил?",
|
|
"expect_reply_contains": ["не знаю"],
|
|
"expect_reply_lacks": ["рад ", "милый", "ваш"]
|
|
},
|
|
{
|
|
"at": "08:55",
|
|
"note": "stating a fact writes it and says so, in the feminine",
|
|
"say": "я выпил воды",
|
|
"expect_reply_contains": ["записала"],
|
|
"expect_reply_lacks": ["записал,", "милый"],
|
|
"expect_events": ["water"]
|
|
},
|
|
{
|
|
"at": "09:00",
|
|
"note": "a second tick, still nothing unprompted",
|
|
"tick": true,
|
|
"expect_no_send": true
|
|
}
|
|
]
|
|
}
|