Skip to content

Commit 9c64a70

Browse files
authored
release: 0.6.8 — a database connection the server closed is reopened (#245)
The fix from #244, cut for release so the hosted image can pick it up: it installs agami-core from PyPI at the floor its pyproject states, so nothing downstream sees this until a version is published. Version bumped in all four places — the package, both plugin manifests and the marketplace entry — and the changelog entry records the three bounds on the retry, each of which was a real defect in an earlier draft rather than caution for its own sake.
1 parent 7a67557 commit 9c64a70

4 files changed

Lines changed: 51 additions & 4 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
},
88
"metadata": {
99
"description": "The trust layer between AI and your data. A governed semantic model for AI-to-database access — every join approved, every metric signed off, every answer with a receipt. Local-first: no infra, no servers, no data leaves your machine.",
10-
"version": "0.6.7",
10+
"version": "0.6.8",
1111
"pluginRoot": "./plugins",
1212
"tags": ["data", "sql", "governance", "trust-layer", "semantic-model", "local-first", "fair-code"],
1313
"repository": "https://github.com/AgamiAI/agami-core",
@@ -18,7 +18,7 @@
1818
"name": "agami-core",
1919
"source": "./plugins/agami",
2020
"description": "A governed trust layer between AI and your database: every join FK-derived or human-approved, every metric signed off, every answer with a receipt (the SQL, the model version, who vouched for each definition). Runs entirely on your machine via Claude's Bash / Read / Write tools — no MCP server or Python required if you have psql/mysql or DuckDB (an optional local MCP server is available for use from Claude Desktop).",
21-
"version": "0.6.7",
21+
"version": "0.6.8",
2222
"keywords": ["database", "governance", "trust-layer", "semantic-model", "sql", "postgres", "snowflake"],
2323
"category": "data"
2424
}

CHANGELOG.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,53 @@ below corresponds to one such version.
1212

1313
## [Unreleased]
1414

15+
## [0.6.8] — 2026-08-27
16+
17+
### Fixed
18+
19+
- **A database connection the server closed is now reopened, instead of poisoning the process until
20+
it is replaced.** `Store` opened one Postgres connection at startup and held it for the life of the
21+
process. A deployment that sits idle for about an hour has that connection reaped from the server
22+
side, and nothing told the process: `execute` called `conn.cursor()` with no liveness check and
23+
there was no reconnect path anywhere in the package.
24+
25+
The instance was then poisoned for as long as it lived — **every** request, not only the ones that
26+
touch data, answered in about three milliseconds having attempted no round trip at all. The three
27+
milliseconds are the tell. A downstream deployment hit this four times in four days, one to two
28+
hours each, across two revisions; thirty days of that service's error logs contained 123 tracebacks
29+
and every one was this.
30+
31+
It fails closed on all traffic because the first thing a request does is resolve the caller's org,
32+
and that read goes down the dead connection — before authentication, before routing. Somebody who
33+
has just signed in successfully sees a connector error, which reads as a login failure and gets
34+
reported as one.
35+
36+
`Store` now keeps the URL it connected with and retries once when the connection is gone. Three
37+
things bound that retry, and each of them was a real defect in an earlier draft rather than
38+
caution for its own sake:
39+
40+
- **It refuses while an uncommitted write is outstanding.** Callers write two rows that must land
41+
together — a role change and the audit line naming who granted it. If the connection dies between
42+
them the first is already lost, and reconnecting silently would let the second commit **alone**:
43+
an audit line for a grant that never happened, which is exactly what that line exists to make
44+
impossible.
45+
- **A read does not count as an outstanding write**, and that distinction is the difference between
46+
fixing this and appearing to. The path this exists for reads five tables in a row and never
47+
commits, because there is nothing to commit. Counting reads would have reconnected once and then
48+
refused for the life of the process.
49+
- **`OperationalError` alone is not enough to act on.** It is a broad base class — a cancelled
50+
query, a statement timeout, a full disk. Retrying on any of them re-runs the statement, and for
51+
the first write of a transaction that lands the row twice. Only the driver marking the connection
52+
closed counts. `InterfaceError` needs no such test, since the driver raises it only on a
53+
connection it has already closed.
54+
55+
It also refuses while a session-scoped advisory lock is held, so a reconnect part-way through a
56+
migration cannot continue without the lock and let a second instance apply the same files.
57+
58+
`Store.rollback()` is new, and callers that reached past it into `store.conn.rollback()` now go
59+
through it — a rollback that bypassed it would leave the connection marked mid-transaction forever
60+
and unable to reconnect again.
61+
1562
## [0.6.7] — 2026-08-18
1663

1764
### Added

packages/agami-core/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ build-backend = "setuptools.build_meta"
1111

1212
[project]
1313
name = "agami-core"
14-
version = "0.6.7"
14+
version = "0.6.8"
1515
description = "agami-core — governed semantic model, the shared MCP TOOLS harness, and the unified local query executor."
1616
readme = "README.md"
1717
requires-python = ">=3.10"

plugins/agami/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "agami-core",
33
"description": "The trust layer between AI and your database, so an agent's answers are defensible. Every join, metric, and named filter is confidence-scored and either FK-derived or human-approved via a review dashboard. Every answer ships a SQL receipt showing what ran and which model version it used. Works across Postgres / MySQL / Snowflake / BigQuery / Redshift / SQL Server / Oracle / Databricks / Trino / DuckDB / SQLite. Local-first: credentials, schema, results never leave your machine.",
4-
"version": "0.6.7",
4+
"version": "0.6.8",
55
"author": {
66
"name": "Agami AI",
77
"email": "skills@agami.ai",

0 commit comments

Comments
 (0)