Repository navigation
Result Contract Stability
Since noetl-tools v4.0.0 (noetl/ai-meta#330).
ToolResult is sealed. Downstream crates can no longer build one with a struct
literal:
// no longer compiles outside this crate
let r = ToolResult { status, data, error, /* … */ };
// use the constructors + builders instead
let r = ToolResult::error("boom")
.with_data(json!({"k": "v"}))
.with_exit_code(1)
.with_duration(5)
.with_pending_callback(true)
.with_child_execution_id("42");Every field is reachable through a builder — a test asserts it, because sealing without that would move the breakage from "downstream cannot compile" to "downstream cannot express what it needs".
Adding a public field to a struct that downstream crates construct by literal
is a breaking change by Rust's rules — and nothing in the release pipeline knows
that. semantic-release reads the commit prefix, so a feat: ships it as a
minor.
That is exactly what 3.27.0 did with child_execution_id: it did not compile
in noetl-executor, and the breakage was latent — waiting for whoever next
resolved a caret range, far from the change that caused it.
One breaking release buys non-breaking field additions forever.
tool_result_is_sealed_against_downstream_literals reads the source rather than
exercising behaviour. It has to: the property is about what other crates may
do, and inside the defining crate a literal is still legal — so a unit test
passes with or without the attribute.
⚠ Do not "simplify" it into a substring search. The first version did exactly
that, and the doc comment on ToolResult explains the attribute by name — so a
mutation removing #[non_exhaustive] left the test green, matching prose. It
now strips comment lines and requires the attribute on a line of its own.
Adding a field is a minor. Add a with_* builder for it in the same change,
or the field is unreachable from outside this crate.
- Central record: noetl/ai-meta#330.
-
child_execution_iditself: noetl/ai-meta#328 — the playbook tool records which execution it spawned, set on all return paths, carried besidedataso the child's result contract is untouched.