diff --git a/emrg/server/daemon.py b/emrg/server/daemon.py index 4a0afc56..4dc76a4c 100644 --- a/emrg/server/daemon.py +++ b/emrg/server/daemon.py @@ -714,6 +714,16 @@ def _with_batch_call_nudge( states it at the tail of the next request, where the next round's calls are actually decided. + It is a **system** message, not a user one. The content is what the model + reads; the role is what every *other* reader reads. A user turn is the + host's voice: it is the role a client renders as the host's bubble, the role + a transcript attributes to the host, and the role a compaction summarises as + something the host said. This reminder is the daemon's instruction to the + model, so a user role tells every one of those readers that the host said + something the host never typed (reported 2026-11-03). A trailing system + message is accepted where it is needed: measured against the configured + provider, `{"role": "system"}` after the user turn answers normally. + A **new list** is returned and `messages` is never touched, and that is the whole of "the reminder exists only in this request": the loop's own list is what `_log_llm_exchange` writes and what a rebuild is rebuilt from, so a @@ -727,7 +737,7 @@ def _with_batch_call_nudge( """ request = [dict(m) for m in messages] if previous_round_tool_calls == 1: - request.append({"role": "user", "content": BATCH_CALL_NUDGE}) + request.append({"role": "system", "content": BATCH_CALL_NUDGE}) return request diff --git a/tests/test_batch_call_nudge.py b/tests/test_batch_call_nudge.py index 08884bdc..828c5caf 100644 --- a/tests/test_batch_call_nudge.py +++ b/tests/test_batch_call_nudge.py @@ -6,8 +6,11 @@ requirement stated in the prompt sits too far from the point where the next round's calls are decided. -So the daemon appends `{"role": "user", "content": "batch call"}` to the -**request** that follows a round dispatching exactly one call. The readings here +So the daemon appends `{"role": "system", "content": "batch call"}` to the +**request** that follows a round dispatching exactly one call. A **system** turn +and not a user one: the reminder is the daemon's instruction, while a user turn +is the host's voice — the role a client renders as the host's bubble and a +transcript attributes to the host. The readings here are the `messages` lists the provider was actually handed — captured where the stream is consumed — so nothing asserts the shape of the code that built them: removing the append, inverting its condition, or moving it ahead of the tool @@ -36,7 +39,7 @@ from emrg.session import Session SESSION_ID = "batch-call-nudge" -NUDGE = {"role": "user", "content": BATCH_CALL_NUDGE} +NUDGE = {"role": "system", "content": BATCH_CALL_NUDGE} class _StubTool: @@ -124,7 +127,7 @@ async def fake_summary(session, records, source=None): def _nudges(messages: list[dict]) -> int: - return sum(1 for m in messages if m.get("role") == "user" + return sum(1 for m in messages if m.get("role") == "system" and m.get("content") == BATCH_CALL_NUDGE)