Semantic versioning policy¶
ssik follows SemVer 2.0: MAJOR.MINOR.PATCH where MAJOR is breaking, MINOR is additive, PATCH is bug-fix only.
What's public, what's not¶
Public (covered by semver)¶
- Top-level imports:
ssik.Manipulator,ssik.Solution,ssik.Diagnostic,ssik.TolerancePolicy,ssik.DEFAULT_TOLERANCE_POLICY - Postprocess helpers:
ssik.postprocess.{respect_limits, wrap_to_limits, nearest_to_seed, take_first} - Streaming IK (since 8.1.0):
ssik.Tracker,ssik.TrackerStep,ssik.TrackStatus,ssik.RedundancyandManipulator.tracker: the constructor and method signatures (includingTracker.redundancyandTracker.set_redundancy), the step's and the redundancy record's fields, the status values and hold reasons, and the rules indocs/api.md("Streaming IK", including "Self-motion") that decide them - Teleoperation frames (since 8.1.0):
ssik.teleop.{PoseSource, Clutch, calibration_from, apply_calibration, tcp_to_flange, flange_to_tcp, scale_about, invert}and the frame conventions indocs/api.md("Teleoperation frames"), including both clutch conventions and the defaultframe="world" - Per-arm preparation (since 8.2.0):
Manipulator.prepare(), itsNonereturn, and the cache rules indocs/api.md("Per-arm preparation cache"): theSSIK_DERIVATION_CACHEvariable and itsoffvalue, the default directory, that an entry misses rather than differing from a fresh derivation, and that concurrent processes may share the directory. File names and formats inside it are not covered - Prebuilt artifacts (
ssik.prebuilt.*): theirsolve(T, **kwargs)signature, the four module constantsBASE_LINK / EE_LINK / DOF / T_HOME, and the per-module__all__ - CLI:
ssik build / classify / add-armargument shapes - Wheel manifest: support for cp311 / cp312 / cp313 × Linux x86_64 / macOS arm64 / macOS x86_64 / Windows x86_64
- C++ headers for native consumers (see below):
ssik.get_include(),ssik.get_cmake_dir(),ssik.cpp.joint_data()/ssik.cpp.JointData, and a named part of the shippedssik_cppheaders - Native solver data (since 8.2.0):
ssik.cpp.solver_data(),ssik.cpp.SolverDataand its fieldsfamily,schema,dofandarrays,ssik.cpp.SOLVER_DATA_SCHEMA,ssik.cpp.NATIVE_FAMILIESandssik.cpp.UnsupportedFamilyError(aValueError, with itsfamilyattribute), and which armssolver_datarefuses (docs/specs/solver_data.md, "Which arms have solver data"). The array names, shapes, dtypes and meanings insidearraysare covered only through the schema number: they are those ofdocs/specs/solver_data.mdat thatschema, and changing them for an existing family bumpsSOLVER_DATA_SCHEMA, which is MAJOR. Adding a family toNATIVE_FAMILIESis MINOR and does not bump the schema
C++ surface (since 7.0.0)¶
Every wheel ships the ssik_cpp headers. Semver covers only this part of them:
- Package: the CMake package
ssik_cpp(fromssik.get_cmake_dir()orcmake --installofcpp/), its targetssik::ssik_cpp, and the include pathssik_cpp/solvers/three_parallel.hpp. - Entry point:
ssik::three_parallel_artifact_solve(const JointConsts<6>&, const JointLimits<6>&, const Pose&, const ArtifactParams<6>&), for the UR family (ikgeo.three_parallel). With the arrays ofssik.cpp.joint_data()it returns the set Python'ssolve()returns with the same options, under the rules above. - Types it takes and returns, in namespace
ssik:Pose,JointConsts<N>,JointType,JointLimits<N>,ArtifactParams<N>,SeedMetric,Solution<N>,Refinement. Their names, field names, field types and meaning, enumerator values, and theArtifactParamsdefaults are covered. Adding a field with a default is MINOR. - Every native family (since 8.2.0): the headers
ssik_cpp/solver_data.hpp,ssik_cpp/solve.hppandssik_cpp/pybind11/solver_data.hpp, and in themSolverData,DataArray,DType,kSolverDataSchema,Solver(its constructor,dof(),family(),solve<N>()andfk<N>()),SolverDataError,UnsupportedSolverData,native_families()andpybind11_interop::solver_data_from_python. Covered with them: whatSolverchecks and throws, that it accepts exactlykSolverDataSchema, and the guarantee indocs/api.md("The native backend") thatSolver(solver_data(arm))returns what ssik's native backend returns.
Not covered: the other families' *_artifact_solve functions (reach them through Solver), the defaulted trailing Tolerances argument (pass none), every detail and *_detail namespace, every other function, type and header in ssik_cpp/, the generated cpp/gen/<arm>_ik.hpp artifacts, and binary compatibility (the headers are compiled into your code, so rebuild when ssik changes). Eigen is the consumer's; ssik tests against the release pinned in scripts/fetch_eigen.py (3.4.0).
Not public (no semver guarantee)¶
- Anything in
ssik.solvers.*(useManipulator.solveor a prebuilt) - Anything in
ssik.core.*except names explicitly re-exported from the top level ssik.kinematics.*(POE primitives, predicates, reverse-chain math)ssik.subproblems.*(SP1–SP6 implementations)ssik.refinement.*(LM polish internals)ssik.codegen.*(artifact emission internals)- Leading-underscore modules:
ssik._kinbody,ssik._urdf,ssik._pencil,ssik._version - The
_solve_algebraic,_KB,_LOCK_IDX,_LOCK_SAMPLES,_DISPATCH_CACHEetc. in prebuilt artifacts (private to the codegen). For an artifact's geometry — itsKinBody, its self-motion charts — the supported route isManipulator.from_prebuilt("<arm>"), whosesolveis the artifact's own solver (#587)
If a downstream depends on a non-public name, that's a "fragile dependency" the user owns; we may rename or remove it in any release.
What counts as breaking¶
A change is MAJOR (breaking) when:
- A public symbol is removed, renamed, or moved to a different module
- A public function's signature changes in a non-backward-compatible way (positional arg order, removed kwarg, kwarg name change)
- A returned dataclass loses a field, or an existing field changes type
- A bug fix that produces materially different IK behaviour for valid inputs (e.g. a previously-returned candidate is now filtered, or a previously-empty result is now populated). Sub-machine-precision FK shifts within the documented tolerance policy are NOT breaking.
- A change to a documented solve contract in
docs/api.md. That includes which point of a singular continuum a seeded or unseeded solve returns ("Singular continua", since 8.0.0), which inputssolve()accepts and what it raises for the rest ("Input validation", since 8.0.0), the limit band, the angle representatives, which status and configuration aTrackerupdate orset_redundancyreturns ("Streaming IK", since 8.1.0), and the teleoperation frame conventions ("Teleoperation frames", since 8.1.0). - The CLI's
--flagsemantics change in a way that breaks scripts pinning a major - Python version support drops (e.g. dropping cp311)
- Wheel-platform support drops in a way that affects existing installs (e.g. dropping macOS arm64)
A change is MINOR (additive) when:
- A new top-level symbol is added
- A new optional kwarg with a backward-compatible default is added
- A new
Solution/Diagnosticfield is added (existing code that doesn't reference the new field keeps working) - A new prebuilt arm is added to
ssik.prebuilt - A new platform is added (e.g. Linux aarch64 wheels)
- A new CLI subcommand or flag with a backward-compatible default is added
A change is PATCH when:
- Bug fixes that don't change documented behaviour
- Numerical precision improvements within the documented tolerance policy
- Performance improvements
- Documentation corrections
- Internal refactors that don't touch the public surface
Build artifacts¶
The codegen-emitted artifacts under ssik.prebuilt (and any user-built <arm>_ik.py) are byte-stable across same-version regenerations (enforced by tests/test_artifact_snapshots.py). Across versions:
- PATCH bump: artifacts MAY differ byte-wise (e.g. updated docstring text, tighter constants) but the public surface (
solve,fk,BASE_LINK,EE_LINK,DOF,T_HOME) stays compatible. - MINOR bump: same, plus new optional
solve()kwargs may appear. - MAJOR bump: artifacts may need re-running
ssik buildagainst your URDF.
User-emitted artifacts are frozen at the ssik version that built them. They keep working with future ssik versions — just won't pick up later solver improvements until you re-run ssik build.
Release-tag conventions¶
v1.0.0,v1.1.0,v1.2.3etc. are real releases (published to PyPI).v1.1.0rc1,v1.1.0rc2etc. are release candidates published to TestPyPI for validation before promotion to PyPI.- Annotated tags only (
git tag -a); the release workflow needs the tag annotation for hatch-vcs version derivation.
Deprecation policy¶
- A public symbol marked deprecated in version
X.Y.Zis removed no earlier than versionX+1.0.0. - Deprecations are surfaced via
DeprecationWarningat the first use of the symbol in a Python session. - Migration paths are documented in the release notes and in the symbol's docstring.
Reporting breakage¶
If a PATCH or MINOR release breaks your code, that's a semver bug — file an issue with a minimal reproducer. A fix or a clarification on what's actually public will follow.