Rolling versioning: 0.MINOR.PATCH, incremented as work lands. A major version bump
(1.0.0, 2.0.0, ...) is a deliberate decision made by a human — never automated.
We work one release at a time. Don't start tasks from a later release until the current one is closed out, unless explicitly reprioritized.
Current release: v0.7.0 (in progress)
docs/ site skeleton (index.html frontpage, synth-behavior.md, this roadmap)synth-behavior.pdf from the markdown sourcesrc/esp32)docs/ on main)Actuator interface (range, rate limit, cost estimate, safety envelope)Sensor interface (normalized numeric channel, update rate)Verified: both board targets (esp32dev, esp32-s3-devkitc-1) build clean with
PlatformIO. Not yet verified against real hardware — pin choices per board are a
starting point, expect to override them per physical chassis.
Verified: both board targets build the firmware and the LittleFS image clean with PlatformIO. Not yet run on real hardware. "Drive targets" config is deferred to v0.4.0, once drives exist to configure — for now the dashboard's manual actuator sliders stand in for direct output control.
h_energy, h_fatigue, h_safety, h_arousal,
h_curiosity, h_boredom, h_social)Verified: both board targets build clean with PlatformIO; a 20 Hz behavior
tick runs Physiology::update() and the dashboard's new Physiology section
shows live value/drive per variable plus fuzz_scale. Not yet run on real
hardware, so the tuning constants (decay/recovery/coupling rates) are
starting points, not calibrated values. h_social has no sensor source yet
(needs another robot/human present) and sits at rest until a later release.
h_curiosity's novelty signal is a placeholder (proportional to raw sensor
change) — real prediction error arrives with contingency memory in v0.5.0.
MAX_ENTRIESVerified: both board targets build clean with PlatformIO (RAM use +7KB for
the 256-entry table). Not yet run on real hardware. The article's pseudocode
doesn't specify how a sensor-delta pattern gets linked to "led to improving
drives" — that's left to whoever queries the memory. This implementation
makes it concrete: each entry tracks an EMA of drive-pressure change
(mean_drive_delta) alongside the action/sensor codes, so query_bias() can
rank matches by whether they historically helped. query_bias() is
implemented and exposed on the dashboard, but nothing calls it yet — no
action generator exists until v0.7.0, so today the table only learns from
whatever a human does via the dashboard sliders or the heartbeat blink.
Verified: both board targets build clean with PlatformIO. Not yet run on
real hardware. A significant, deliberate departure from the article's
pseudocode: this hardware has no positioning sensor (no encoders/IMU/GPS),
and the article's spatial_bias(position, drives, spatial_map) assumes
position already exists without saying where it comes from. Rather than
fabricate open-loop odometry from commanded motor duty cycle — which would
drift into meaninglessness within seconds, since duty cycle isn't measured
displacement — this uses the article's own explicitly offered alternative
(§8.1): "a coarse 2-D grid OR a set of place cells". A place cell here is a
coarse signature of current sensor readings; same ambient conditions are
treated as the same place, which is the same gradient-following idea §10
already uses for individual channels (light, RF), just made the identity of
a location instead of a single bias term.
Consequence: best_cell() (the "spatial bias contribution" task) answers
which remembered place looks most promising for current drives, but not
which direction to move — there's no direction without real coordinates.
Turning that into actual motor output is deferred to v0.7.0's core loop,
which will need a concrete policy for it (most likely: bias exploration
persistence/exploitation balance rather than literal steering, given the
hardware). Currently, with only 2 sensor channels on both board profiles,
signature entropy is low (few distinguishable "places") — richer
differentiation needs richer sensor profiles, which is a board-config
change, not an engine change.
ActionGenerator is the piece every prior release was missing: physiology,
contingency memory, and spatial memory now actually drive the actuators each
tick (baseline + drive-scaled noise + contingency bias + spatial nudge,
clamped) instead of only observing whatever the dashboard did. A channel
recently touched from the dashboard (Actuator::manual_override_active,
3s window) is left alone rather than fought over — the heartbeat blink uses
the same mechanism (write_manual) so it stays system-owned without a
hardcoded channel-name exception in the engine.
Task split: high-frequency work (PWM via hardware LEDC, ADC via on-demand
cached Sensor::read()) needs no separate task — the hardware and existing
on-demand reads already satisfy it. Behavior task is the existing 20 Hz
tick. Slow task is the new StateLogger, ~1 Hz, appending physiology +
total actuator cost to a 64KB-capped /log.csv on the same LittleFS
partition the dashboard serves from — downloadable at /log.csv with zero
extra server code, and exposed as a button on the dashboard's Config tab.
Fixed-point arithmetic: evaluated and deliberately not done. The article's advice (§14.2) targets microcontrollers generically; both ESP32 and ESP32-S3 have a hardware single-precision FPU, so float ops in a tick this small (a few hundred multiply-adds across four modules, worst case) cost microseconds — replacing them with fixed-point would trade real readability for no measurable benefit on this specific hardware. Revisit only if a future board target lacks an FPU.
Honest limitation carried over from v0.6.0: SpatialMemory::best_cell()
still can't say which direction to move, only which remembered place
looks best. ActionGenerator resolves that the way flagged there — as an
explore/exploit nudge (wider noise if a better place than "here" is known,
narrower if "here" already looks best), not fabricated steering.
Verified: both esp32dev and esp32-s3-devkitc-1 build clean (firmware + LittleFS image) with PlatformIO. Dashboard changes (manual-override badge, log download link) verified against a mocked API in a real browser. Not yet run on real hardware — the gains/constants above (noise, bias, and nudge scaling) are starting points, not tuned values.
battery_voltage/current as ordinary sensor channels feeding h_energyh_energy)