Goal
Make Phase safe and verifiable for the first public v0.1.0 release.
This plan is based on the release review of main at commit 00b6ac9a6b4660f83c2547795d84e6421ad0414e.
Release blockers
1. Make task teardown ownership race-free
The worker currently clears taskRunning and taskHandle before it has actually finished executing and deleted/suspended itself. Phase::end() may therefore return and allow _impl destruction while the worker still accesses PhaseImpl.
2. Reject end() from the Phase task
Callbacks execute synchronously on the Phase task. Calling end() from a lifecycle callback, group condition, onChange, onReady, or onFailed can make the task wait for itself.
3. Fix callback string lifetime guarantees
PhaseChange::pauseReason points into mutable internal storage. Another task can call resume() while the callback is running and invalidate the pointer.
4. Remove lifecycle-time dynamic allocations
Runtime snapshot objects currently copy std::string, dependency vectors, and std::function instances during boot/readiness/shutdown.
5. Add behavioral test coverage
Current CI compiles examples across multiple ESP32 targets but does not validate state-machine behavior.
6. Align release metadata with the tag
Correctness and observability fixes
7. Correct stack high-water-mark units
ESP-IDF reports uxTaskGetStackHighWaterMark() in bytes. Phase currently multiplies it by sizeof(StackType_t).
8. Define shutdown failure semantics
Stop/deinit callback failures are currently emitted but discarded while shutdown still ends in Stopped.
Choose and document one policy:
or
Also:
API and documentation pass
Recommended implementation order
- Teardown ownership and worker-exit handshake.
- Self-
end()/destructor policy. - Callback pointer lifetime fixes.
- Runtime snapshot/allocation redesign.
- Behavioral test harness and race tests.
- Stack diagnostic and shutdown-result fixes.
- Metadata, documentation, and release validation.
Release acceptance criteria
v0.1.0 is ready when:
Goal
Make Phase safe and verifiable for the first public
v0.1.0release.This plan is based on the release review of
mainat commit00b6ac9a6b4660f83c2547795d84e6421ad0414e.Release blockers
1. Make task teardown ownership race-free
The worker currently clears
taskRunningandtaskHandlebefore it has actually finished executing and deleted/suspended itself.Phase::end()may therefore return and allow_impldestruction while the worker still accessesPhaseImpl.end()cannot observe completion before the task has stopped accessingPhaseImpl.exitReadyPhaseImplend()deletes the worker task using the stored handleend()from Idle, Booting, Starting, Ready, Paused, Stopping, Failed, and Stopped.2. Reject
end()from the Phase taskCallbacks execute synchronously on the Phase task. Calling
end()from a lifecycle callback, group condition,onChange,onReady, oronFailedcan make the task wait for itself.xTaskGetCurrentTaskHandle() == taskHandleinend().end()must be called by another task.end()from every callback type.stop(),pause(), andresume()remain safe when called from callbacks.3. Fix callback string lifetime guarantees
PhaseChange::pauseReasonpoints into mutable internal storage. Another task can callresume()while the callback is running and invalidate the pointer.pauseReasoninto storage whose lifetime covers the complete callback invocation.nodeNameandmessagefor the same guarantee.onChangeis executing.4. Remove lifecycle-time dynamic allocations
Runtime snapshot objects currently copy
std::string, dependency vectors, andstd::functioninstances during boot/readiness/shutdown.std::functionunless the implementation proves the copy is non-allocating and safe.5. Add behavioral test coverage
Current CI compiles examples across multiple ESP32 targets but does not validate state-machine behavior.
start()end()rejection6. Align release metadata with the tag
library.jsonversion to0.1.0.library.propertiesversion to0.1.0.0.0.1to0.1.0.vX.Y.Zmatches both manifests.Correctness and observability fixes
7. Correct stack high-water-mark units
ESP-IDF reports
uxTaskGetStackHighWaterMark()in bytes. Phase currently multiplies it bysizeof(StackType_t).8. Define shutdown failure semantics
Stop/deinit callback failures are currently emitted but discarded while shutdown still ends in
Stopped.Choose and document one policy:
or
onChange().Also:
API and documentation pass
start(),stop(),pause(), andresume().Stopped.end()as terminal and external-task-only.Recommended implementation order
end()/destructor policy.Release acceptance criteria
v0.1.0is ready when:PhaseImplafterend()returns.end()cannot deadlock when invoked from the Phase task.PhaseChangepointers remain valid throughout callback execution.0.1.0.