1 API Stability Contract
John McCardle edited this page 2026-07-11 19:43:36 +00:00

API Stability Contract

mcrf-meta -- objects: Color, Vector, ColorLayer, TileLayer, DiscreteMap | type: system | status: current | verified: 0.2.8 (b6720f6)

Ratified 2026-07-02; enforced mechanically by tests/unit/api_surface_snapshot_test.py; migrated from docs/ on 2026-07-11.

This document records the compatibility promises that freeze at McRogueFace 1.0. Each item below is a semantic decision that cannot be changed compatibly after 1.0 and was ratified in the 2026-07-02 pre-1.0 memory-model review. Additive, opt-in APIs may still be introduced after 1.0; the guarantees here constrain what those additions may assume.

The public mcrfpy surface (types, methods, properties, enum members, functions, singletons, and each property's inferred type / read-only flag) is guarded against accidental drift by tests/unit/api_surface_snapshot_test.py, which diffs the live module against a committed golden file. That test is the mechanical enforcement of this policy; any intentional change to the surface must be a reviewed re-baseline of the golden file.

Value semantics for Color and Vector (#326)

mcrfpy.Color and mcrfpy.Vector are value types, forever. They store their data inline (no shared references), and every property that returns a Color or Vector returns a fresh copy. This matches how sf::Color / sf::Vector2f behave in C++ and carries zero implementation risk. Write-through proxy views were considered and rejected: they would freeze dangling-parent lifetime and identity questions into the contract.

The direct consequence is that mutating a component of a color/vector obtained from a property does not affect the original — it mutates a throwaway copy:

frame.fill_color.r = 255   # NO-OP: mutates a temporary copy, then discards it

The two supported idioms are:

# 1. Read-modify-writeback
c = frame.fill_color       # copy
c.r = 255                  # mutate the copy
frame.fill_color = c       # write the whole value back

# 2. Whole-value assignment
frame.fill_color = mcrfpy.Color(255, 0, 0)

This behavior is stated normatively in the Color and Vector class docstrings and will not change in the 1.x series.

Bulk-edit convention for writable views (#328)

Future zero-copy writable views over cell/buffer data (numpy / buffer protocol) are exposed only through an edit() context manager:

with layer.edit() as view:
    view[...] = ...        # write freely
# on __exit__, the affected state is conservatively invalidated
# (whole-layer markDirty / full TCOD resync + generation bump as applicable)

Forgetting to synchronize is impossible by construction, which applies the project's fail-early principle to the API surface. The engine performs no automatic change detection; invalidation is triggered unconditionally on __exit__.

  • Read-only zero-copy views (for example DiscreteMap.mask()) may still be exposed directly, with no edit() ceremony, because they cannot violate engine invariants.
  • No always-writable raw view + mark_dirty() primitives will be shipped. If a genuine need for persistent writable views emerges after 1.0, such primitives can be added additively without breaking the edit() idiom.

This convention is the pattern that the buffer-protocol work must follow: ColorLayer/TileLayer buffers (#335), related view APIs (#334), and any future grid.walkable array view (which depends on the GridData SoA refactor, #332).

Subinterpreters are out of scope for 1.x (#330)

Running mcrfpy in a Python subinterpreter is unsupported in 1.x; the module declares m_size = -1 and will refuse or misbehave. See #220.

The module uses single-phase initialization with global state (static PyTypeObjects, cached enum singletons, and global engine pointers), so it cannot be safely instantiated per-subinterpreter. Excluding subinterpreters from the 1.0 promise leaves the later refactor (#220: heap types, multi-phase init, per-interpreter module state) unconstrained. Heap-type conversion does not change Python-visible behavior, so nothing else needs to be reserved for it.