Skip to content

Commit bfc87a3

Browse files
fix: skip registry republish for tools never exported on source
Empty Prompt Studio projects cannot be exported (backend guard), so they have no source registry entry and no workflow references. The phase previously republished unconditionally, turning these into counted failures on every run. Now the source registry lookup runs first and the export + remap are skipped when no entry exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent d34f5ee commit bfc87a3

2 files changed

Lines changed: 52 additions & 2 deletions

File tree

src/unstract/clone/phases/custom_tool.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
5. Republishes ``PromptStudioRegistry`` via the export action and
1919
records the ``custom_tool`` + ``prompt_studio_registry`` remaps so
2020
downstream ToolInstancePhase can rewrite ``ToolInstance.tool_id``.
21+
Skipped for tools with no source registry entry (never exported —
22+
e.g. empty projects, which the backend refuses to export).
2123
2224
Adapter id discovery for the fresh path needs all four of LLM,
2325
vector_db, embedding, x2text. If any source adapter can't be resolved
@@ -163,6 +165,30 @@ def _clone_one(
163165
if self.ctx.options.dry_run:
164166
return
165167

168+
# Tools never exported on source (e.g. empty projects — backend
169+
# blocks their export) have no registry entry and no workflow
170+
# references; republishing would fail the same backend guard.
171+
try:
172+
src_regs = self.ctx.source.list_registries(custom_tool=src_tool_id)
173+
except Exception as e:
174+
logger.warning(
175+
"source registry lookup failed for tool '%s' "
176+
"(downstream ToolInstance clone may skip): %s",
177+
tool_name,
178+
e,
179+
)
180+
with lock:
181+
result.failed += 1
182+
result.errors.append(f"registry remap lookup {tool_name}: {e}")
183+
return
184+
185+
if not src_regs:
186+
logger.info(
187+
"tool '%s' was never exported on source; skipping registry republish",
188+
tool_name,
189+
)
190+
return
191+
166192
try:
167193
self.ctx.target.export_custom_tool(tgt_tool_id)
168194
logger.info(
@@ -176,11 +202,10 @@ def _clone_one(
176202
return
177203

178204
try:
179-
src_regs = self.ctx.source.list_registries(custom_tool=src_tool_id)
180205
tgt_regs = self.ctx.target.list_registries(custom_tool=tgt_tool_id)
181206
except Exception as e:
182207
logger.warning(
183-
"registry remap lookup failed for tool '%s' "
208+
"target registry lookup failed for tool '%s' "
184209
"(downstream ToolInstance clone may skip): %s",
185210
tool_name,
186211
e,

tests/clone/test_custom_tool_phase.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,31 @@ def test_frictionless_adapter_dependence_skips_tool_and_records_for_cascade():
363363
assert SRC_REG in ctx.skipped_custom_tool_registry_ids
364364

365365

366+
def test_never_exported_source_tool_skips_registry_republish():
367+
"""A source tool with no registry entry (e.g. an empty project — the
368+
backend blocks exporting those) clones cleanly without republishing.
369+
"""
370+
src = FakeClient()
371+
tgt = FakeClient()
372+
_preload_source_tool(src, "src-tool-x", "Empty Project")
373+
del src.registries_by_tool["src-tool-x"]
374+
src.export_blobs["src-tool-x"]["prompts"] = []
375+
_seed_source_adapters(src)
376+
_seed_target_adapters(tgt)
377+
ctx = _ctx(src, tgt)
378+
379+
result = CustomToolPhase(ctx).run(CloneReport())
380+
381+
assert result.created == 1
382+
assert result.failed == 0
383+
# No registry on source → republish must not fire (it would hit the
384+
# backend's empty-project export guard).
385+
assert tgt.export_tool_calls == []
386+
# Tool remap still recorded; registry remap absent.
387+
assert ctx.remap.resolve("custom_tool", "src-tool-x") is not None
388+
assert ctx.remap.resolve("prompt_studio_registry", SRC_REG) is None
389+
390+
366391
def test_missing_target_adapter_fails_tool_cleanly():
367392
src = FakeClient()
368393
tgt = FakeClient()

0 commit comments

Comments
 (0)