Skip to content

Fix two live bugs reported in the PR backlog - #173

Merged
adamjohnwright merged 4 commits into
mainfrom
fix/live-bugs-from-pr-backlog
Sep 4, 2026
Merged

Fix two live bugs reported in the PR backlog#173
adamjohnwright merged 4 commits into
mainfrom
fix/live-bugs-from-pr-backlog

Conversation

@adamjohnwright

Copy link
Copy Markdown
Contributor

Two defects that were still present on main, both reported by contributors. Neither contributor PR is merged as-is; see below.

Alliance MITAB columns — reported by @AaryanCode69 in #120

Adjacent string literals concatenate silently in Python, so a missing comma turns two column names into one. molecular_interaction had three such sites, collapsing seven columns into three:

"Interaction detection method(s) Publication 1st author(s)"   -> 2 columns
"Host organism(s)Interaction parameter(s)"                    -> 2 columns
"Annotation(s) interactor A" ... "Creation date"              -> 6 columns

Those metadata fields were never populated on Alliance documents.

genetic_interaction is the same MITAB schema written correctly, so it serves as an oracle: both lists are now 41 columns with identical sets.

#120 caught two of the three. The third — "Interaction detection method(s) Publication 1st author(s)" — was a single literal containing a space rather than an implicit concatenation, so it did not appear in that diff.

Why this is guarded by a test, not a lint rule

ruff's ISC001 flags implicit concatenation on one line, but it is disabled because ruff itself warns it conflicts with the formatter. And running ruff format joined "Host organism(s)" "Interaction parameter(s)" into a single literal, destroying the syntax a rule could match on. Two tests instead: one comparing the MITAB lists against each other, one rejecting any column name that begins with another. Both were run against the pre-fix source to confirm they fail on the real bug rather than passing vacuously.

AgentGraph.del — reported by @bleedblack1 in #156 and @bhavyakeerthi3 in #147

__del__ called asyncio.run() unconditionally, which raises RuntimeError when a loop is already running — and __del__ fires at arbitrary moments, including inside the running server. Exceptions there are swallowed and printed, so it appeared as log noise with the pool still open.

Neither proposed fix is taken as-is:

This closes the pool only when no loop is running, and otherwise warns that it is still open rather than pretending otherwise. Errors from the close are caught, because __del__ must not raise.

This is containment, not a cure. Nothing calls close_pool() explicitly, so __del__ is the only cleanup that exists, and it is fundamentally unreliable — writing the test surfaced that even the warning fails at interpreter shutdown once logging's handlers are closed. The real fix is closing the pool from an application shutdown hook, which belongs with the agent-API work.

Tests

97 total, up from 91. Four cover the destructor: loop running, no loop, no pool, and a close that raises.

Closes #120. Supersedes #147 and #156.

🤖 Generated with Claude Code

adamjohnwright and others added 4 commits September 4, 2026 20:49
Adjacent string literals concatenate silently in Python, so a missing comma
turns two column names into one. `molecular_interaction` had three such sites,
collapsing seven columns into three:

  "Interaction detection method(s) Publication 1st author(s)"   -> 2 columns
  "Host organism(s)Interaction parameter(s)"                    -> 2 columns
  "Annotation(s) interactor A" ... "Creation date"              -> 6 columns

Those metadata fields were never populated on Alliance documents.
`genetic_interaction` is the same MITAB schema written correctly, and the two
lists now match exactly: 41 columns each, identical sets.

Reported by @AaryanCode69 in #120, which caught two of the three sites. This
also fixes the "Interaction detection method(s) Publication 1st author(s)" case,
which was a single literal with a space rather than an implicit concatenation
and so did not appear in that diff.

A linter cannot guard this. ruff's ISC001 flags implicit concatenation on one
line, but it is disabled because it conflicts with the formatter -- and running
`ruff format` earlier today joined `"Host organism(s)" "Interaction parameter(s)"`
into a single literal, destroying the syntactic evidence that a rule could have
matched. So the guard is a test instead: one comparing the two MITAB lists
against each other, and one rejecting any column name that begins with another
column name. Both were checked against the pre-fix source to confirm they fail
on the real bug rather than passing vacuously.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
__del__ called asyncio.run() unconditionally, which raises RuntimeError when a
loop is already running -- and __del__ fires at arbitrary moments, including
inside the running server. Exceptions in __del__ are swallowed and printed, so
it surfaced as log noise with the pool still open.

Reported independently by @bleedblack1 in #156 and @bhavyakeerthi3 in #147.

Neither proposed fix is taken as-is. #147 schedules the close with
loop.create_task(), but a task created from __del__ is not guaranteed to run if
the loop is shutting down, which is exactly when a graph is usually collected --
so it reports success while leaking the pool. #156 rewrites the module more
broadly than the bug warrants.

Instead: close the pool only when there is no running loop, and otherwise warn
that it is still open rather than pretend otherwise. Errors from the close are
caught, because __del__ must not raise.

This is containment, not a cure. Nothing calls close_pool() explicitly, so
__del__ is the only cleanup that exists, and it is fundamentally unreliable --
writing the test surfaced that even the warning fails at interpreter shutdown,
once logging's handlers are closed. The real fix is closing the pool from an
application shutdown hook, which belongs with the agent-API work.

Four tests cover the loop-running case, the no-loop case, no pool at all, and a
close that raises.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
preprocess made three LLM calls back to back. The safety check needs the
rephrased text so it must follow the rephrase, but language detection reads the
raw user input and does not -- so those two now run concurrently, saving one
round trip on every message, on the path every profile shares.

Idea from @bleedblack1 in #111. That patch parallelised the same two calls; this
is the same change written against current main, since the PR predates 34
commits and its diff no longer applies.

Two tests, both checked against the sequential version to confirm they fail on
it: one asserting the two calls actually overlap in wall-clock time, one
asserting the rephrase still completes before the safety check starts, which is
the ordering constraint that must not be lost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PT018: the overlap check asserted two conditions at once, so a failure would not
say which interval was wrong. Split, with a message for each.

Caught by CI rather than locally, because the local check had its output
redirected to /dev/null and the && chain short-circuited silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant