KN-86 NoshAPI Versioning Contract
Overview
Section titled “Overview”The NoshAPI is the function table that the nOSh runtime exposes to capability modules (cartridges). This document specifies how the nOSh runtime and cartridges negotiate API compatibility using version numbers, an append-only vtable, and rejection semantics.
Version Fields
Section titled “Version Fields”nOSh runtime: api_version
Section titled “nOSh runtime: api_version”Each nOSh runtime build declares an api_version (uint16_t, semver-packed: high byte = major, low byte = minor).
- Major bump: A function signature changed, a function was removed, or semantics changed incompatibly. Old cartridges targeting the previous major version will not work.
- Minor bump: A new function was appended to the vtable. Old cartridges still work; they simply don’t call the new function.
Current: 0x0100 (v1.0 — initial release)
nOSh runtime: min_api_version
Section titled “nOSh runtime: min_api_version”The oldest cartridge API version this nOSh runtime supports (uint16_t, same format).
- Cartridges with
req_api_version < nosh_runtime.min_api_versionare rejected. - This allows the nOSh runtime to deprecate and eventually drop old API functions.
- Guideline: Support at least N-2 minor versions. Only bump
min_api_versionwhen a deprecated function is removed from the vtable.
Current: 0x0100 (supports all v1.x cartridges)
Cartridge: req_api_version
Section titled “Cartridge: req_api_version”Each cartridge header declares the minimum API version it requires (uint16_t, semver-packed).
- If
cartridge.req_api_version > nosh_runtime.api_version, the nOSh runtime cannot satisfy the cartridge’s requirements — reject. - If
cartridge.req_api_version < nosh_runtime.min_api_version, the cartridge uses deprecated API calls the nOSh runtime no longer supports — reject.
Append-Only Vtable Rules
Section titled “Append-Only Vtable Rules”The NoshAPI vtable is an ordered array of function pointers. The order is the contract.
- New functions are appended at the end. Never insert in the middle; never reorder existing entries.
- Existing function signatures are immutable within a major version. A change to parameters, return type, or documented behavior requires a major version bump.
- Existing function semantics are stable. Bug fixes that match documented behavior are fine. Changes that alter observable behavior require a new function (appended) and deprecation of the old one.
- Removed functions leave a tombstone. When a function is deprecated and eventually removed, its vtable slot becomes a no-op stub that logs a warning. The slot is never reused.
Example
Section titled “Example”vtable v1.0 (api_version = 0x0100): [0] text_clear [1] text_putc [2] text_puts ... [37] draw_bordered_box
vtable v1.1 (api_version = 0x0101): [0] text_clear ← unchanged [1] text_putc ← unchanged ... [37] draw_bordered_box ← unchanged [38] new_function_a ← appended [39] new_function_b ← appendedA v1.0 cartridge calls slots [0]–[37] only. When running on v1.1 nOSh runtime, those slots are unchanged — the cartridge works. Slots [38]–[39] exist but the cartridge never calls them.
A v1.1 cartridge may call slots [0]–[39]. If loaded on v1.0 nOSh runtime, the load check sees req_api_version (0x0101) > nosh_runtime.api_version (0x0100) and rejects the cartridge.
Cartridge Rejection Behavior
Section titled “Cartridge Rejection Behavior”On Load (before cart_init)
Section titled “On Load (before cart_init)”The nOSh runtime checks version compatibility immediately after reading the cartridge header:
if (cart.req_api_version > nosh_runtime.api_version) { /* Cartridge needs a newer nOSh runtime */ reject("MODULE ERROR: requires API v%d.%d (nOSh runtime has v%d.%d)", MAJOR(cart.req_api_version), MINOR(cart.req_api_version), MAJOR(nosh_runtime.api_version), MINOR(nosh_runtime.api_version));}
if (cart.req_api_version < nosh_runtime.min_api_version) { /* Cartridge uses deprecated API calls */ reject("MODULE ERROR: API v%d.%d no longer supported (min: v%d.%d)", MAJOR(cart.req_api_version), MINOR(cart.req_api_version), MAJOR(nosh_runtime.min_api_version), MINOR(nosh_runtime.min_api_version));}Error Messages
Section titled “Error Messages”Clear, operator-facing messages displayed on the main screen:
| Condition | Message |
|---|---|
| Cart too new | MODULE ERROR: requires API vX.Y (nOSh runtime has vA.B) |
| Cart too old | MODULE ERROR: API vX.Y no longer supported (min: vA.B) |
| Unknown version format | MODULE ERROR: invalid API version field |
Recovery
Section titled “Recovery”After rejection:
- The cartridge is not loaded (no
cart_initcalled). - The emulator returns to the bare deck / mission board.
- The operator can swap to a compatible cartridge.
Forward Compatibility Guideline
Section titled “Forward Compatibility Guideline”The nOSh runtime should support at least N-2 API versions where N is the current minor version.
| nOSh runtime API | min_api_version | Supported cartridge range |
|---|---|---|
| v1.0 | v1.0 | v1.0 only |
| v1.1 | v1.0 | v1.0 – v1.1 |
| v1.2 | v1.0 | v1.0 – v1.2 |
| v1.3 | v1.1 | v1.1 – v1.3 (v1.0 dropped) |
| v2.0 | v2.0 | v2.0 only (major break) |
api_version Semantics: What Constitutes a Bump
Section titled “api_version Semantics: What Constitutes a Bump”Minor bump (e.g., v1.0 → v1.1)
Section titled “Minor bump (e.g., v1.0 → v1.1)”- New function appended to vtable
- New optional field added to a struct (with safe zero-default)
- New event type added to input system
- New display mode added
Major bump (e.g., v1.x → v2.0)
Section titled “Major bump (e.g., v1.x → v2.0)”- Function signature changed (parameters, return type)
- Function removed from vtable (slot becomes tombstone)
- Struct layout changed in an incompatible way
- Semantics of an existing function changed
No bump required
Section titled “No bump required”- Bug fixes that match documented behavior
- Performance improvements with identical API surface
- Internal refactoring (implementation detail, not API)
- New nOSh runtime only features (not exposed to cartridges)
Implementation Notes
Section titled “Implementation Notes”Current State
Section titled “Current State”The v1.1 codebase does not yet implement explicit api_version checking. The CartridgeHeader.version field (uint8_t, currently 2) is a header format version, not an API version.
Migration Path
Section titled “Migration Path”When the v2.0 cartridge format lands (per ADR-0001):
- Add
api_version(uint16_t) to the nOSh runtime global state - Add
req_api_version(uint16_t) to the v2.0 CartridgeHeader - Add
min_api_version(uint16_t) to the nOSh runtime global state - Implement the rejection check in
cartridge_load_module()/cartridge_load() - Populate the vtable as an explicit indexed array (not scattered function pointers)
- Document each vtable slot index in this file as the vtable grows
Vtable Slot Registry (v1.0)
Section titled “Vtable Slot Registry (v1.0)”Reserved for when the explicit vtable is implemented. The 54 primitives from ADR-0005-ffi-surface.md will be assigned stable slot indices here.
Three Tiers and Versioning
Section titled “Three Tiers and Versioning”The NoshAPI has three access tiers (per ADR-0005-ffi-surface.md):
| Tier | Scope | Version Interaction |
|---|---|---|
| Tier 1: All-Carts | Always available | Versioned in the main vtable |
| Tier 2: Mission-Context | Active mission phases only | Versioned in the main vtable, guarded by runtime phase state |
| Tier 3: REPL-Read-Only | Player-facing Lisp REPL | Subset of Tier 1; inherits version from main vtable |
Tier membership does not affect versioning. All functions occupy a single append-only vtable regardless of tier. Tier access is a runtime guard, not a compile-time split.
Summary
Section titled “Summary”api_version: nOSh runtime declares; semver-packed uint16_tmin_api_version: oldest cartridge API supported by this nOSh runtimereq_api_version: cartridge declares; must fall within nOSh runtime’s supported range- Vtable: append-only, stable slot indices, tombstones for removed functions
- Rejection: clear error messages, no partial loading, safe recovery to bare deck
- Guideline: support N-2 minor versions; major bumps are clean breaks