Files
Maven/cmd/mavend/testdata/scenarios/morning_missed.json
T
kami 33e53ee897 Add a replayable full-system simulator on a fake clock (#284)
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
2026-08-01 06:15:21 +04:00

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
}
]
}