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:
parent
5bf79d6b92
commit
d2f8319772
|
|
@ -21,6 +21,11 @@ def _read_frontmatter(text: str) -> dict[str, str]:
|
||||||
return out
|
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:
|
def collect_vault_context(vault_path: Path, today: date) -> dict:
|
||||||
persons: list[dict] = []
|
persons: list[dict] = []
|
||||||
persons_dir = vault_path / "00 Kontext" / "Personen"
|
persons_dir = vault_path / "00 Kontext" / "Personen"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .context import collect_vault_context
|
from .context import collect_vault_context, day_context
|
||||||
from .queue import Queue
|
from .queue import Queue
|
||||||
from .vault_writer import VaultWriter
|
from .vault_writer import VaultWriter
|
||||||
from .processor_protocol import ProcessorInput
|
from .processor_protocol import ProcessorInput
|
||||||
|
|
@ -18,9 +18,14 @@ def process_once(processor, queue: Queue, writer: VaultWriter, vault_path: Path,
|
||||||
try:
|
try:
|
||||||
received_dt = datetime.fromisoformat(item.received_at)
|
received_dt = datetime.fromisoformat(item.received_at)
|
||||||
received_time = received_dt.strftime("%H:%M")
|
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(
|
payload = ProcessorInput(
|
||||||
today=ctx["today"],
|
today=send_day["today"],
|
||||||
weekday=ctx["weekday"],
|
weekday=send_day["weekday"],
|
||||||
received_time=received_time,
|
received_time=received_time,
|
||||||
persons=ctx["persons"],
|
persons=ctx["persons"],
|
||||||
projects=ctx["projects"],
|
projects=ctx["projects"],
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ Antworte ausschließlich mit einem JSON-Objekt nach folgendem Schema. Kein Flie
|
||||||
|
|
||||||
# Regeln
|
# 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`.
|
2. **target_path:** Immer `05 Daily Notes/{target_date}.md`.
|
||||||
3. **entry_markdown:** Beginnt IMMER mit `## HH:MM`. Nutze `received_time` aus dem Context.
|
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`.
|
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`.
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,23 @@ class FakeProcessor:
|
||||||
return self._output
|
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"):
|
def make_item(update_id=1, text="Hallo"):
|
||||||
return QueueItem(
|
return QueueItem(
|
||||||
update_id=update_id,
|
update_id=update_id,
|
||||||
|
|
@ -74,6 +91,35 @@ def test_process_fails_item_on_error(tmp_path):
|
||||||
assert data["attempts"] == 1
|
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):
|
def test_process_records_result_fields(tmp_path):
|
||||||
vault = tmp_path / "vault"
|
vault = tmp_path / "vault"
|
||||||
(vault / "05 Daily Notes").mkdir(parents=True)
|
(vault / "05 Daily Notes").mkdir(parents=True)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue