# `ph-haptics` 0.1.0 public API
## Scope
This document covers the runtime library API. Generator CLI internals are
documented separately in [`compiler-design.md`](compiler-design.md).
## Goals
- Keep the runtime crate focused on deterministically evaluating precompiled haptics into timed, abstract motor commands.
- Make the compiled path the default and supported construction path.
- Keep `no_std` / `no_alloc` and deterministic behavior for embedded use.
## Non-Goals
- No textual parsing/compilation in the runtime crate.
- Handwritten programs are not the supported integration path; consumers use
catalogs emitted by `ph-haptics-gen`.
- No allocator-dependent runtime features.
- No GPIO, PWM, bus, HAL, motor-driver, or board-integration code in this crate.
- No claim about electrical safety, actuator compatibility, physical motor response, or HIL results.
## Integration boundary
- Embedded consumers depend on `ph-haptics` with **default features only** (never `features = ["gen"]`).
- Consuming firmware applies `DriveCommand` through its own hardware adapter.
- Host-side compilation stays in `ph-haptics-gen` (`--features gen`).
- Product contract: [`contract.md`](contract.md). Package rules: [`architecture.md`](architecture.md).
## Public API Surface
### Modules
- `compiled`
- `error`
- `motor`
- `runtime`
### Public Re-exports (`src/lib.rs`)
- Compiled model:
- `CompiledHapticDef`
- `CompiledCatalog`
- `CompiledCatalogError`
- DSL (crate-root; used by generated code):
- `Instruction`
- `LoopMode`
- `Program`
- `Ramp`
- Runtime:
- `Runner`
- `Frame`
- Motor/domain:
- `MotorKind`
- `MotorConfig`
- `ErmConfig`
- `LraConfig`
- `MotorProfile`
- `DEFAULT_ERM_PROFILE`
- `DEFAULT_LRA_PROFILE`
- `DriveCommand`
- Error:
- `Error`
- Curves ecosystem passthrough (`ph-curves` 0.2.1, default features off):
- `ph_curves` module itself
- `Curve`
- `MonotonicCurve`
- `MonotonicCurveLut`
- `MonotonicCurveLut256`
- `Rounding`
- `Tickless`
- `TicklessDeadline`
- `TicklessSchedule`
- `UnitValue`
- Do **not** re-export 0.2 transfer / filter / calibration APIs unless the
haptics runtime needs them.
## Primary command flow (supported path)
1. Include generated compiled asset file (from `ph-haptics-gen`).
2. Use generated `COMPILED_CATALOG` (or `CompiledCatalog::new(&COMPILED_HAPTICS)`).
3. Construct a runner through:
- `CompiledCatalog::runner(...)` / `runner_started(...)`, or
- `CompiledHapticDef::runner(...)` / `runner_started(...)`.
4. Evaluate the current clock with `Runner::poll(...)`.
5. Apply `Frame.command` through the consumer's hardware adapter and sleep until
`Frame.next_transition_ms` when appropriate. Deadlines use wrapping `u32`
arithmetic, so wait with a wrap-aware remaining duration
(`deadline.wrapping_sub(now_ms)`) or a rollover-aware timer — do not compare
`now_ms` and the deadline with absolute `<` / `>`. See the wrap-safe tickless
notes in [`compiler-design.md`](compiler-design.md) and
[`AGENTS.md`](https://github.com/photon-circus/ph-haptics/blob/main/AGENTS.md).
## API Contract Details
### `CompiledHapticDef`
- Contains:
- name
- compiled `Program`
- optional `MotorProfile`
- Public construction helpers:
- `new(name, program, profile)`
- `runner(motor)`
- `runner_started(motor, now_ms)`
- Runtime behavior note:
- profile gamma (if present) is applied during command evaluation.
- profile kick (if configured) is applied when scheduled output recovers from rest / level 0.
### `CompiledCatalog`
- Name lookup and runner construction over a definition slice.
- Case-insensitive name search.
- Public helpers:
- `new(definitions)`
- `definitions()`, `is_empty()`
- `find(name)`, `require(name)`
- `runner(name, motor)`, `runner_started(name, motor, now_ms)`
- Error boundary:
- `NotFound`
- wrapped runtime `Error`.
### `Runner`
- Public lifecycle:
- `start`, `restart`, `stop`
- `poll`, `poll_or_start`, `start_and_poll`
- Public state access:
- `is_running`, `motor`, `start_ms`
- Construction is intentionally routed through compiled APIs (catalog/definition).
### `Frame`
- Scheduler output payload:
- `command: DriveCommand`
- `next_transition_ms`
- `instruction_index`
- `finished`
- Helpers:
- `is_idle`, `is_active`, `is_finished`, `command()`, `next_transition_ms()`
### `MotorProfile`
- Runtime command evaluation uses profile information emitted by generator:
- kick (`kick_ms`, `kick_frac`)
- run range (`min_run_frac`, `max_frac`)
- gamma mapping (`gamma_curve`)
- ramp defaults (`ramp_step_ms`, `min_dt_ms`, `duty_step`)
- Built-in defaults: `DEFAULT_ERM_PROFILE`, `DEFAULT_LRA_PROFILE`.
## Compatibility and Stability Notes
- Supported integration contract is:
- `ph-haptics-gen` output + `ph-haptics` runtime + a consumer-owned hardware adapter.
- Generated code imports DSL types via crate-root re-exports (`ph_haptics::{Instruction, LoopMode, Program, Ramp}`).
- Module-level access to `dsl` is intentionally private.
## Contributor validation (local CI)
[`scripts/ci.sh`](https://github.com/photon-circus/ph-haptics/blob/main/scripts/ci.sh)
is the canonical gate list and the
contributor source of truth:
```sh
bash scripts/ci.sh
```
That covers formatting, host tests, generator tests, clippy over all host
targets, bare-metal check/clippy, a no_std/alloc feature guard, catalog
generation over `assets/phh-library/`, a generate→compile roundtrip, and the
mock-machine verify. See [`CONTRIBUTING.md`](../CONTRIBUTING.md).
### Internal-only compatibility surface
- `dsl` module is crate-private (`mod dsl;`) and not part of external module API.
- DSL items needed by generated code are available via root re-exports.
## Change Control Guidance
- Any change to:
- `CompiledHapticDef`,
- `CompiledCatalog`,
- `Runner` construction paths,
- root DSL type re-exports (`Instruction`, `LoopMode`, `Program`, `Ramp`),
- `DEFAULT_ERM_PROFILE` / `DEFAULT_LRA_PROFILE`,
should be treated as a cross-crate contract change and validated with local CI (including the generate→compile roundtrip).