• v0.1.6 7c766ea752

    v0.1.6
    All checks were successful
    CI / cargo (push) Successful in 35s
    CI / docker (push) Successful in 20s
    Stable

    jlxq0 released this 2026-08-29 00:20:02 +00:00 | 8 commits to main since this release

    Adds upcoming_birthdays: contacts whose birthday falls within the next N days,
    across one or all address books, in one call.

    BDAY was parsed nowhere in the service before this, so the question could only
    be answered by paging list_contacts four times against 366 cards over a limit
    of 100 and parsing vCards client-side, which is 5.39 MB of vCard for a
    one-line question
    . ContactSummary also gains a birthday field.

    Accepts every ISO form vCard 3.0 and 4.0 permit and returns nothing on anything
    else, because a card with a free-text BDAY is a card and must not fail a query
    over the whole book. unparseable_birthdays in the response is the only signal
    that an answer is incomplete.

    Accepted in the field, both arms

    days=30   birthdays=3   contacts_scanned=374   unparseable=0   ref=2026-08-29
    days=14   birthdays=0   contacts_scanned=374   unparseable=0   ref=2026-08-29
    

    Expected 3 and 0, predicted before the code existed from a raw
    addressbook-query REPORT rather than from the tool: 45 birthdays across 374
    cards, three inside 30 days at offsets 15, 22 and 30.

    Confirmed independently by the pod's own audit log rather than only by the
    caller: method=upcoming_birthdays outcome=ok result_count=3 latency_ms=431 and
    result_count=0 latency_ms=292. 431 ms to scan 374 cards is the measure of
    what the tool replaces.

    The 14-day arm is a control only because the 30-day arm answers differently
    over the same cards on the same day. Alone it returns an empty list from a
    correct implementation, from a query matching nothing, and from one erroring
    into an empty vector alike.

    What the address book cannot pin

    45 birthdays, none today, none on a leap day, none unparseable. Fixtures pin the
    date rules and each dies under its own mutation: the leap-day choice (29
    February occurs on 1 March in a common year, so a birthday is never reported as
    past while still ahead), the inclusive window edge, the wrap into next year, and
    the birth-year floor.

    That last one is from cross-engine review, which found that a reference_date
    before the birth year treated the month-day as already recurring: 1984-03-17
    asked from 1983-03-16 returned 1 day instead of 367.

    Downloads