-
v0.56.0 Stable
released this
2026-09-21 13:52:32 -05:00 | 52 commits to main since this releaseThe sweep one release ago found almost nothing; this one asked the same question
of the code rather than of the file list, and found four things. The Canvas tool
layer had never logged a line, both mail tenants carried a byte-identical copy of
their whole allowlist layer, aPhasemember had been dead since the first commit,
and fourteen settingsconfig.pyreads were in no example file. A test now catches
the first and third classes of defect, which nothing could see before.Added
mcp_gateway/mail/imap.py—ImapMailbox, the provider-independent half of an
IMAP mail client, plusCachedFoldersandsuppress_close. Fourteen definitions
that both mail tenants held byte-identical copies of: the read and write allowlists,
the folder cache, handle resolution and its UIDVALIDITY check,count_messages,
_localise, the two best-effort post-send steps, and the two preflight checks.
Both clients inherit it and 236 lines of duplication are gone.- The Canvas tool layer logs. Twelve tools, twelve
infolines on success and one
warn/erroron every refusal, routed through_refusalso the failure path is one
line of code rather than twelve copies of one. tests/test_logging.pyguards the logging vocabulary two ways — everyPhase
member must be passed tofor_phasesomewhere, and everylog_x = for_phase(...)
binding must be called in its own file. Both were broken deliberately and observed
failing before being fixed.- Fourteen settings documented in
.env.example, including the first Canvas
section it has ever had:CANVAS_BASE_URL,CANVAS_COOKIE_PATH,
CANVAS_COOKIE_SEED,CANVAS_KEEPALIVE_SECONDS,CANVAS_TIMEOUTand the five
truncation bounds, plusCALDAV_URL,CALDAV_TIMEOUT,HEALTH_PATH,
FASTMCP_HOMEandMAIL_ALIAS_INDEX_TTL.
Removed
Phase.HTTP_HEALTH— declared in the first commit and never once logged under.
/healthzis the container healthcheck, so a line per call is one every few seconds
saying the thing was up, which is the one fact the absence of the container already
reports. The phase is gone rather than the silence being explained beside it.
Fixed
- The Canvas tenant was invisible in Portainer.
canvas/tools.pybound
log_coursesat the top of the file and never called it, so twelve tools — including
every refusal, and including the session expiry that only a person can fix — produced
no log line at all. Nothing could catch this: ruff sees the name as used because the
assignment uses it, mypy has no opinion, and the binding at the top of the file reads
as evidence the module logs. test_a_classmates_profile_never_leaves_canvasfailed 0.84% of runs, and had
since 0.50.0. It searched the whole serialised response for a three-digit user id as
a substring, and the envelope'snowis an ISO timestamp carrying twenty digits that
change every run — so427or909landed inside the microseconds about one CI run
in a hundred, as a PII leak that had not happened. Measured over 200,000 timestamps.
The clock is now dropped before the needles are looked for; nothing else is relaxed.
Notes
- What was NOT de-duplicated, and it is the finding worth keeping.
_take_copyuid,_parse_copyuid,_take_appenduid,_parse_appenduidand
_appendstay in both tenants. Their bodies are identical and their behaviour is
not: each parser closes over its own regex, and the two differ in both directions —
Gmail's admits*in a UID set and matches case-sensitively, iCloud's forbids*
and passesre.IGNORECASE. Unifying them means adopting the union, which widens what
a handle-minting parser accepts on two deployed tenants, and the note on
_take_appenduidasks for both write probes to be re-run first. Identical bodies
are not identical functions when they close over different constants — the fake
agreeing with the code failure in one more disguise, and a body-level duplicate
detector reports it as safe. - The tool-layer duplicates in
gmail/tools.pyandicloudmail/tools.pywere left
alone. Nine pairs share a body, but each is wrapped in adescription=written for
its own tenant — Gmail says "label" where iCloud says "folder" — and those strings are
the model-facing surface. Collapsing the bodies would mean a factory parameterised by
a page of prose, to save about fifty lines._require_writablestays per-tenant for
the same reason: every string it raises is read by a model. - No live probe was re-run. Everything here is covered by the suite, and nothing
touches a request this project makes to Apple, Google, Canvas or ESPN — but the write
paths on both mail tenants now run through inherited methods rather than their own
copies, so the next mail live pass is the first observation of that.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads