fix(process): derive target date from message send day, not processing day

A message sent yesterday but processed today (e.g. by the next service
run) landed in today's daily note. The send time was captured in
received_at but only HH:MM reached the LLM; the date defaulted to
date.today(). Now today/weekday in the processor context are derived per
item from received_at, so "gestern"/"heute" resolve relative to when the
message was actually written. Adds day_context() helper and a regression
test (sent 2026-06-25, processed 2026-06-26 -> note 2026-06-25).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
beo3000 2026-06-26 08:19:29 +02:00
parent 5bf79d6b92
commit d2f8319772
4 changed files with 60 additions and 4 deletions

View File

@ -21,6 +21,11 @@ def _read_frontmatter(text: str) -> dict[str, str]:
return out
def day_context(d: date) -> dict:
"""today/weekday for a given date (the message's send day)."""
return {"today": d.isoformat(), "weekday": WEEKDAYS_DE[d.weekday()]}
def collect_vault_context(vault_path: Path, today: date) -> dict:
persons: list[dict] = []
persons_dir = vault_path / "00 Kontext" / "Personen"

View File

@ -1,6 +1,6 @@
from datetime import date, datetime
from pathlib import Path
from .context import collect_vault_context
from .context import collect_vault_context, day_context
from .queue import Queue
from .vault_writer import VaultWriter
from .processor_protocol import ProcessorInput
@ -18,9 +18,14 @@ def process_once(processor, queue: Queue, writer: VaultWriter, vault_path: Path,
try:
received_dt = datetime.fromisoformat(item.received_at)
received_time = received_dt.strftime("%H:%M")
# Default day is the message's SEND day, not the processing day,
# so a message sent yesterday but processed today lands in
# yesterday's note. "gestern"/"heute" in the text are relative
# to the send day. Fall back to `today` if received_at is unusable.
send_day = day_context(received_dt.date())
payload = ProcessorInput(
today=ctx["today"],
weekday=ctx["weekday"],
today=send_day["today"],
weekday=send_day["weekday"],
received_time=received_time,
persons=ctx["persons"],
projects=ctx["projects"],

View File

@ -15,7 +15,7 @@ Antworte ausschließlich mit einem JSON-Objekt nach folgendem Schema. Kein Flie
# Regeln
1. **target_date:** Standard = `today` aus dem Context. Bei „gestern" → today 1, „vorgestern" → today 2. Bei explizitem Datum (z.B. „am 12. Juni") dieses verwenden.
1. **target_date:** Standard = `today` aus dem Context. `today` ist der **Sendetag der Nachricht** (nicht der Verarbeitungstag), also der Tag, an dem Christian die Nachricht geschrieben hat. Bei „gestern" → today 1, „vorgestern" → today 2 (relativ zum Sendetag). Bei explizitem Datum (z.B. „am 12. Juni") dieses verwenden.
2. **target_path:** Immer `05 Daily Notes/{target_date}.md`.
3. **entry_markdown:** Beginnt IMMER mit `## HH:MM`. Nutze `received_time` aus dem Context.
4. **Meta-Anweisungen entfernen:** Phrasen wie „Schreib ins Journal, dass …", „Notier dir, dass …", „Merk dir …" gehören NICHT in den Eintragstext. Liste sie in `raw_excluded`.

View File

@ -22,6 +22,23 @@ class FakeProcessor:
return self._output
class CapturingProcessor:
"""Records the payload it was asked to process and echoes today as target."""
def __init__(self):
self.payload = None
def health_check(self):
return True
def process(self, payload):
self.payload = payload
return ProcessorOutput(
target_date=payload.today,
target_path=f"05 Daily Notes/{payload.today}.md",
entry_markdown=f"## {payload.received_time}\n{payload.text}",
)
def make_item(update_id=1, text="Hallo"):
return QueueItem(
update_id=update_id,
@ -74,6 +91,35 @@ def test_process_fails_item_on_error(tmp_path):
assert data["attempts"] == 1
def test_target_date_follows_send_day_not_processing_day(tmp_path):
# Message sent yesterday (2026-06-25), processed today (2026-06-26).
# The daily note must be the send day, not the processing day.
vault = tmp_path / "vault"
(vault / "05 Daily Notes").mkdir(parents=True)
queue = Queue(tmp_path / "q")
item = QueueItem(
update_id=1,
received_at="2026-06-25T08:13:42+02:00",
type="text",
text="Neue Charge Kefir angesetzt",
)
queue.enqueue(item)
processor = CapturingProcessor()
writer = VaultWriter(vault)
process_once(
processor, queue, writer,
vault_path=vault,
today=date(2026, 6, 26), # processing day differs from send day
processed_at="2026-06-26T06:55:22+02:00",
)
# The processor must have been told today = the send day, with matching weekday.
assert processor.payload.today == "2026-06-25"
assert processor.payload.weekday == "Donnerstag" # 2026-06-25 is a Thursday
# And the entry landed in the send-day note.
data = json.loads((tmp_path / "q" / "done" / "1.json").read_text(encoding="utf-8"))
assert data["target_path"] == "05 Daily Notes/2026-06-25.md"
def test_process_records_result_fields(tmp_path):
vault = tmp_path / "vault"
(vault / "05 Daily Notes").mkdir(parents=True)