fix: preserve real artifact node definitions

This commit is contained in:
lda
2026-08-31 01:48:27 +07:00 Verified
parent d4ca8ec6a6
commit faa62e4878
2 changed files with 51 additions and 8 deletions
+9 -8
View File
@@ -259,18 +259,19 @@ def _seed_remote_node_defs(
requirement.output_schema_snapshot, requirement.output_schema_snapshot,
output_fields, output_fields,
) )
if name in builder.seeded_node_defs:
# A contract explicitly carried by the plan is authoritative even
# when the server omitted dependency snapshots for this capability.
continue
if not isinstance(requirement.input_schema_snapshot, dict) or not isinstance( if not isinstance(requirement.input_schema_snapshot, dict) or not isinstance(
requirement.output_schema_snapshot, dict requirement.output_schema_snapshot, dict
): ):
permissive_names.add(name) permissive_names.add(name)
builder.seeded_node_defs.setdefault( builder.seeded_node_defs[name] = NodeDef(
name, name=name,
NodeDef( input_schema=SchemaRef.model_validate(input_schema),
name=name, output_schema=SchemaRef.model_validate(output_schema),
input_schema=SchemaRef.model_validate(input_schema), outcomes=outcomes_by_node.get(name, ["ok"]),
output_schema=SchemaRef.model_validate(output_schema),
outcomes=outcomes_by_node.get(name, ["ok"]),
),
) )
return permissive_names return permissive_names
+42
View File
@@ -93,6 +93,22 @@ def remote_plan_without_schema_snapshots(version: int = 1) -> dict[str, Any]:
return payload return payload
def remote_plan_with_real_node_def(version: int = 1) -> dict[str, Any]:
payload = remote_plan_without_schema_snapshots(version)
payload["plan"]["node_defs"] = [
{
"name": "app.default.remote",
"input_schema": {
"type": "object",
"properties": {"real": {"type": "string"}},
},
"output_schema": {"type": "object", "properties": {}},
"outcomes": ["ok"],
}
]
return payload
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_validate_stops_before_remote_call_when_local_graph_is_invalid() -> None: async def test_validate_stops_before_remote_call_when_local_graph_is_invalid() -> None:
port = FakePort() port = FakePort()
@@ -206,3 +222,29 @@ async def test_snapshotless_remote_node_upgrades_to_real_capability_contract() -
) )
with pytest.raises(ValueError, match="incompatible duplicate"): with pytest.raises(ValueError, match="incompatible duplicate"):
graph.use(incompatible, id="incompatible", input=[], output=[]) graph.use(incompatible, id="incompatible", input=[], output=[])
@pytest.mark.asyncio
async def test_real_plan_node_def_remains_authoritative_without_snapshots() -> None:
port = FakePort()
port.inspect_artifact_result = remote_plan_with_real_node_def(version=1)
graph = await App._from_port(cast(WorkflowClientPort, port)).edit_workflow(
"report", version=1
)
incompatible = RemoteCapability(
_port=cast(WorkflowClientPort, port),
ref=CapabilityRef.parse("app.default.remote"),
qualified_name="app.default.remote",
description=None,
input_schema={"type": "object", "properties": {"other": {"type": "string"}}},
output_schema={"type": "object", "properties": {}},
outcomes=("ok",),
is_async=False,
)
with pytest.raises(ValueError, match="incompatible duplicate"):
graph.use(incompatible, id="incompatible", input=[], output=[])
assert graph.seeded_node_defs["app.default.remote"].input_schema.properties == {
"real": {"type": "string"}
}