You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Corrected 1 Sep 2026. This issue originally opened by claiming all three changes were
available on hotdata_framework>=0.10.0 with "no SDK bump, no upstream PR". That is wrong
for two of them, and the error was inherited from docs/ddl-tools-plan.md:5, whose
self-containment claim rests on the same false premise. Corrected inline below; see the
comments for the version table that pinned it down.
Three changes. mode is free today; format and result_id are not — see the correction
under item 2. The tool signatures are narrower than the calls they make, but in two cases the
framework client is narrower than the SDK model as well.
Context: an internal audit (18 Aug) counted 3 tools exposed against 8 framework capabilities
available but unwrapped. This issue is the subset an agent that provisions its own storage needs
first; the broader lifecycle question stays in #61.
1. hotdata_create_managed_database drops four parameters
The tool takes (name, schema_name, tables). The framework call underneath takes keys, expires_at, partition_by and sorted_by as well.
Two of those are load-bearing for an agent that provisions its own storage:
expires_at — accepts RFC 3339 or a relative "24h" / "7d". Without it, every database an
agent creates lives forever, and lifetime becomes a cleanup script instead of a property. This is
the whole answer to "who deletes investigation number 4,281".
keys — declares each table's natural key, which is what makes the key-based load modes
legal on it. A table declared through the tool today is keyless, so upsert, update and delete loads are rejected against it for the rest of its life.
⚠️ Note partition_by / sorted_by are permanent once a table exists — there is no ALTER path
anywhere in the API, and the only undo is delete-and-reload, which currently burns the table name
(#36). Whatever the tool description says about these two should state that outright, in the same
"state the consequence, not just the parameter" style as today's load tool.
databases.load_managed_table never passes mode, so the framework default applies and append, upsert, update and delete are unreachable from a tool. An agent cannot top up a
table it already loaded, and cannot make a re-run idempotent.
⚠️format and result_id are not reachable from the framework client on any version. hotdata_framework/client.py:396 builds LoadManagedTableRequest(mode=…, upload_id=…, key=…)
— three fields — and load_managed_table is the only load path on the client. Checked across
0.10.0, 0.11.0, 0.12.0, 0.12.1 and 0.13.0: absent from all five. So these two need a framework
change, or a raw-DatabasesApi bypass, and are not in the same "pure wrapping" bucket as mode.
A working bypass already exists downstream, if it helps whoever picks this up: the
connection-scoped route fails with X-Database-Id header is required, and DatabasesApi(client.api).load_database_table(database_id=…, var_schema=…, table=…, load_managed_table_request=LoadManagedTableRequest(mode="replace", result_id=…)) works. That
said, a framework change is the cleaner fix in a package that goes through the client
everywhere else.
The underlying LoadManagedTableRequest accepts both, and they are worth having:
result_id publishes a persisted query result as a table's contents — effectively CREATE TABLE AS SELECT, including across an attached catalog. Worth flagging: the DDL memo
recorded query materialization as "found nowhere, needs upstream design" after looking in ResultsApi, SavedQueriesApi and QueryRunsApi. It exists — it is just on the load endpoint.
3. Mark the destructive tools so interrupt_on can be configured
HumanInTheLoopMiddleware(interrupt_on=…) is keyed by tool name, so a developer wiring approval
has to know which HDLC tool names are mutating. Today that is inferred from naming.
Export DESTRUCTIVE_TOOL_NAMES from the package so it can be passed straight through.
Set metadata={"destructive": True} on the mutating tools — BaseTool supports tags and metadata, settable via StructuredTool.from_function, and it echoes MCP's destructiveHint.
Worth stating plainly in the docs while doing this: management_tools=False is not a safety
flag (the source already says so), and "read-only" currently appears only in tool descriptions
written for the model — there is no validator on the SQL path in this library. DDL through SQL is
blocked by runtimedb's write_guard.rs rather than by anything here, and that guard does not
cover the Databases/Indexes/Jobs REST endpoints.
Three changes.
modeis free today;formatandresult_idare not — see the correctionunder item 2. The tool signatures are narrower than the calls they make, but in two cases the
framework client is narrower than the SDK model as well.
Context: an internal audit (18 Aug) counted 3 tools exposed against 8 framework capabilities
available but unwrapped. This issue is the subset an agent that provisions its own storage needs
first; the broader lifecycle question stays in #61.
1.
hotdata_create_managed_databasedrops four parametersThe tool takes
(name, schema_name, tables). The framework call underneath takeskeys,expires_at,partition_byandsorted_byas well.Two of those are load-bearing for an agent that provisions its own storage:
expires_at— accepts RFC 3339 or a relative"24h"/"7d". Without it, every database anagent creates lives forever, and lifetime becomes a cleanup script instead of a property. This is
the whole answer to "who deletes investigation number 4,281".
keys— declares each table's natural key, which is what makes the key-based load modeslegal on it. A table declared through the tool today is keyless, so
upsert,updateanddeleteloads are rejected against it for the rest of its life.partition_by/sorted_byare permanent once a table exists — there is no ALTER pathanywhere in the API, and the only undo is delete-and-reload, which currently burns the table name
(#36). Whatever the tool description says about these two should state that outright, in the same
"state the consequence, not just the parameter" style as today's load tool.
2.
hotdata_load_managed_tablehardcodesmode="replace"databases.load_managed_tablenever passesmode, so the framework default applies andappend,upsert,updateanddeleteare unreachable from a tool. An agent cannot top up atable it already loaded, and cannot make a re-run idempotent.
formatandresult_idare not reachable from the framework client on any version.hotdata_framework/client.py:396buildsLoadManagedTableRequest(mode=…, upload_id=…, key=…)— three fields — and
load_managed_tableis the only load path on the client. Checked across0.10.0, 0.11.0, 0.12.0, 0.12.1 and 0.13.0: absent from all five. So these two need a framework
change, or a raw-
DatabasesApibypass, and are not in the same "pure wrapping" bucket asmode.A working bypass already exists downstream, if it helps whoever picks this up: the
connection-scoped route fails with
X-Database-Id header is required, andDatabasesApi(client.api).load_database_table(database_id=…, var_schema=…, table=…, load_managed_table_request=LoadManagedTableRequest(mode="replace", result_id=…))works. Thatsaid, a framework change is the cleaner fix in a package that goes through the client
everywhere else.
The underlying
LoadManagedTableRequestaccepts both, and they are worth having:formatremoves a pyarrow step from every caller materialising data it already holds (this isgap 2 in Provisioning boundary: agent-driven database, table, and index lifecycle #61).
result_idpublishes a persisted query result as a table's contents — effectivelyCREATE TABLE AS SELECT, including across an attached catalog. Worth flagging: the DDL memorecorded query materialization as "found nowhere, needs upstream design" after looking in
ResultsApi,SavedQueriesApiandQueryRunsApi. It exists — it is just on the load endpoint.3. Mark the destructive tools so
interrupt_oncan be configuredHumanInTheLoopMiddleware(interrupt_on=…)is keyed by tool name, so a developer wiring approvalhas to know which HDLC tool names are mutating. Today that is inferred from naming.
DESTRUCTIVE_TOOL_NAMESfrom the package so it can be passed straight through.metadata={"destructive": True}on the mutating tools —BaseToolsupportstagsandmetadata, settable viaStructuredTool.from_function, and it echoes MCP'sdestructiveHint.Worth stating plainly in the docs while doing this:
management_tools=Falseis not a safetyflag (the source already says so), and "read-only" currently appears only in tool descriptions
written for the model — there is no validator on the SQL path in this library. DDL through SQL is
blocked by
runtimedb'swrite_guard.rsrather than by anything here, and that guard does notcover the Databases/Indexes/Jobs REST endpoints.