fix(hurtig tilføjelse): fire ting, der blev læst forkert eller tabt i stilhed

Fundet under arbejdet med genopbygnings-prompten, hvor den rigtige parser
blev kørt over en tabel af sætninger.

- "at" før et 12-timers klokkeslæt blev stående i titlen: "file the VAT
  return tomorrow at 10am" blev opgaven "file the VAT return at". Markøren
  (at, kl, klokken) hører nu med til tiden, også foran "7:30 pm".
- "køb 1/2 liter fløde" fik forfaldsdato 1. februar. En d/m efterfulgt af en
  mængdeenhed (liter, dl, kg, stk, tsk, kop, del, …) er en mængde. Uden enhed
  er det stadig en dato — "3/4" alene, "møde 3/4 med Anders" og også "3/4
  kage", fordi intet i "kage" skiller tredje april fra tre fjerdedele, og en
  dato, der stille forsvinder, er værre end en, man selv sletter.
- Et "#navn" eller "/navn", der ikke fandtes, blev klippet ud af titlen, og
  opgaven landede et andet sted uden et ord. Serveren har længe sendt
  unknown_project og unknown_section; fladen siger dem nu i en besked, også
  når en underopgave beholder sin opgaves projekt.
- Titlen i opgaveruden blev læst om, men gentagelsen blev læst under
  `parsed.recurrence`, et felt der ikke findes, så "hver mandag" forsvandt.
  Den læser nu `recurrence_rule` og tager også klokkeslæt, projekt og alarm
  med, og siger gentagelsen på læserens sprog i stedet for et fast
  "gentagelse". Alarmen lægges før rettelsen, så den skrevne ikke stables
  oven på standardalarmen.

Prøver: to nye Go-prøver i quickadd og to e2e-prøver. Fem plantede fejl,
hver fanget af sin egen prøve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kristianwind
2026-10-31 00:32:16 +01:00
co-authored by Claude Opus 5.5
parent ec5cf8f9f9
commit ed30eb4c36
7 changed files with 249 additions and 13 deletions
+47
View File
@@ -784,3 +784,50 @@ func TestImpossibleReminderClockIsNotKept(t *testing.T) {
t.Errorf("ReminderOffsetMin = %d, want none", *got.ReminderOffsetMin)
}
}
// A marker written before a 12-hour time belongs to the time. Left behind, it
// became the end of the title: "file the VAT return tomorrow at 10am" was the
// task "file the VAT return at".
func TestMarkerBeforeAMeridiemTimeIsConsumed(t *testing.T) {
now := ref(t)
for _, tc := range []struct{ in, content, due, time string }{
{"file the VAT return tomorrow at 10am", "file the VAT return", "2026-03-11", "10:00"},
{"call at 7:30 pm", "call", "2026-03-10", "19:30"},
{"call the dentist tomorrow at 9am remind me an hour before", "call the dentist", "2026-03-11", "09:00"},
{"møde i morgen kl 7 pm", "møde", "2026-03-11", "19:00"},
// Control: a meridiem time with no marker still works, and "at" that is
// not in front of a time is a word.
{"standup tomorrow 9am", "standup", "2026-03-11", "09:00"},
{"look at the report tomorrow", "look at the report", "2026-03-11", ""},
} {
got := Parse(tc.in, now, "en")
if got.Content != tc.content || got.DueDate != tc.due || got.DueTime != tc.time {
t.Errorf("%q → %q %s %s, want %q %s %s", tc.in, got.Content, got.DueDate, got.DueTime,
tc.content, tc.due, tc.time)
}
}
}
// "1/2 liter" is half a litre. A day/month followed by a unit is a quantity and
// stays in the title; without a unit it is still a date, because "3/4" alone and
// "møde 3/4 med Anders" are the third of April.
func TestAFractionWithAUnitIsNotADate(t *testing.T) {
now := ref(t)
for _, tc := range []struct{ in, content, due string }{
{"køb 1/2 liter fløde", "køb 1/2 liter fløde", ""},
{"tilsæt 3/4 dl mælk", "tilsæt 3/4 dl mælk", ""},
{"bag med 1/2 kg mel i morgen", "bag med 1/2 kg mel", "2026-03-11"},
// A quantity first, a real date after it: the date is still found.
{"køb 1/2 liter fløde 15/3", "køb 1/2 liter fløde", "2026-03-15"},
// Controls: no unit, or a year — a date.
{"møde 3/4", "møde", "2026-04-03"},
{"møde 3/4 med Anders", "møde med Anders", "2026-04-03"},
{"halvdelen 3/4 kage", "halvdelen kage", "2026-04-03"},
{"frist 1/2-2027 liter", "frist liter", "2027-02-01"},
} {
got := Parse(tc.in, now, "da")
if got.Content != tc.content || got.DueDate != tc.due {
t.Errorf("%q → %q %q, want %q %q", tc.in, got.Content, got.DueDate, tc.content, tc.due)
}
}
}