Skip to content

ABI stability

The protocol carries a single version number, FZNSO_ABI_VERSION, currently 1.

Every solver exports it as fznso_<name>_abi_version. Every application checks it before calling any other entry point, and rejects the solver outright if it differs from its own.

Version 1 and version 2 do not interoperate, in either direction. The interface is a set of structs with function pointers at fixed offsets, so a mismatch is not a missing feature but a call through the wrong pointer; detecting it costs one symbol lookup per library, and refusing is the only safe response.

Because the check happens per candidate rather than per directory, solvers built for different revisions can share an installation directory. A loader resolving a solver by name skips a candidate whose version does not match and continues the search, so upgrading one solver never breaks the others.

Any change that alters the meaning or layout of what crosses the interface:

  • adding, removing or reordering a field in FznsoModelMethods, FznsoValueMethods, FznsoSolutionMethods or FznsoAnnotationMethods, or in any other struct in fznso_types.h;
  • adding, removing or reordering a variant of FznsoValueKind, FznsoTypeBase or FznsoStatus;
  • adding, removing or changing the signature of any of the thirteen entry points;
  • changing the contract of an existing callback — when it may be called, what it may return, what the caller may do with the result.
  • Anything in the registry. Adding a constraint, option, objective, statistic or message scope changes no layout and no signature. A solver that does not implement a new entry simply does not declare it, and applications discover that through the capability lists.
  • A solver’s own release version. See Naming & versioning.
  • Documentation that clarifies without changing behaviour.

Return the constant from the headers you built against, rather than a literal:

uint32_t fznso_mysolver_abi_version(void) { return FZNSO_ABI_VERSION; }

A rebuild against a newer fznso_types.h then reports the new version automatically, and the mismatch is caught at load time rather than becoming a crash inside a callback. The Rust fznso_export! and C++ FZNSO_EXPORT_SOLVER macros both do this for you.