• v0.36.0 acb9479c73

    joeyr released this 2026-08-23 18:18:26 -05:00 | 97 commits to main since this release

    G7: get_thread, and the Gmail tenant is complete at twenty-one tools. The
    one tool this provider can support and icloudmail cannot — iCloud advertises
    no threading, so the same tool there would mean guessing at References chains.
    It needs no setting: it reads, so it registers under the read allowlist beside
    the other seven. Live pass in docs/operations.md — 20 checks, preceded by a
    probe that decided the implementation rather than confirming it.

    Added

    • get_thread. Every message in one conversation, oldest first, each one
      exactly once, with spans_labels naming every label the conversation touches.
      Oldest-first is the one list on this tenant that is not newest-first, because
      a conversation printed newest-first puts the answers above the questions.

    • It gathers from every readable label, and that is the phase.
      probes/gmail_probe_thread.py measured a thread spanning labels in 12 of 12
      sampled cases, six with members in Sent Mail — so a thread read out of one
      label is half a conversation. And it measured the Bin and Spam sitting
      OUTSIDE All Mail
      , twice: a message in either was unreachable by
      X-GM-MSGID from All Mail, and a member binned mid-probe dropped out of the
      gather with its thread id intact. All Mail holds every member exactly once,
      which is what makes it the tempting gather point and what would have made the
      defect invisible — a conversation returned with the deleted message silently
      missing. The gather is search_messages' instead, keyed on the thread id, so
      a member in the Bin comes back and says the Bin.

    • An unsearchable label is named in the response, not just logged. A search
      that misses a folder returns fewer of many results; a thread that misses one
      is missing a participant, and the remainder still reads as a complete
      exchange. _thread_partial is its own copy of _partial rather than a call
      to it: that wording is about a window cut by UID and tells the model to narrow
      the query or raise limit, and a thread has neither.

    • A capped thread always contains the message that was asked about. Keeping
      the newest is right and would, alone, answer a question about one message with
      a list not containing it — the root of a long thread being exactly what "what
      is this about" is asked from. Matched by X-GM-MSGID, not by the handle,
      because the gather may meet that message under a different label first.

    Fixed

    • get_thread's description no longer promises the whole conversation.
      Gmail threads on References and a matching subject, and takes both — a
      true In-Reply-To reply with two words added to its subject gets a thread of
      its own. The probe had tested each half separately, seen both come back
      separate, and that was read as References is what threads; probes/g7_diag.py
      found the third case, surfacing as three red checks in the live pass against
      code that was working
      . The fixture had named its binned member Re: … root (deleted), so the message the section existed to bin was never in the thread
      it was being looked for in. The rule: this server returns the provider's own
      grouping and does not invent a better one — reassembling across a renamed
      subject would hand the user a conversation Gmail does not have.

    Changed

    • docs/gmail-plan.md is deleted, by the rule written at the top of it: it
      held only what was unbuilt, and nothing is. What was durable moved to
      CLAUDE.md — Why destroying is its own tool and not a boolean, cited from
      both mail clients, and Ruled out for the mail tenants, which is the list
      get_thread itself came off. The new A conversation is the provider's holds
      what this phase measured.

    • The Gmail surface test asserts twenty-one tools rather than "and not
      get_thread". That assertion turned red five times on purpose — G5, G3, G4,
      G6, G7 — and keeps its keep for the opposite reason now: the set is complete,
      so anything appearing in it is a tool nobody counted.

    Downloads