Finding #3 of the 2026-08 architecture review. Partially addressed in #52; the remaining half is breaking.
Problem
MutexInterface.releaseLock() returns MaybePromise<boolean>, where false means the release failed. Statemachine.releaseLock() discards that boolean and is declared Promise<void> (src/interfaces/StatemachineInterface.ts:31).
The engine's automatic release path was fixed in #52: a false return is now treated exactly like a thrown release error, reported to onReleaseError, and raised as LockCanNotBeReleasedError. But callers doing manual lock management — the documented batch pattern in docs/mutex.md, autoreleaseLock: false — call sm.releaseLock() directly. For them, #52 could only add the diagnostic hook; the return value is still dropped, so the straightforward reading of
await sm.releaseLock(); // resolved, so the lock is free, right?
is still wrong when the underlying adapter reports failure. Detecting it requires either wiring onReleaseError or interrogating the mutex, neither of which is discoverable from the signature.
Why it was deferred
Widening Promise<void> to Promise<boolean> is source-compatible for callers who ignore the result, but it breaks anyone implementing StatemachineInterface — their releaseLock(): Promise<void> no longer satisfies the interface. That is a major-version change.
Proposed direction
Change both the class and StatemachineInterface to releaseLock(): Promise<boolean>, returning what the mutex reported. Throwing LockCanNotBeReleasedError instead is the alternative; it is more consistent with the automatic path, but it turns a previously silent situation into an exception in existing user code, so the boolean is the gentler option. Pick one and document it in docs/mutex.md alongside the release-failure semantics added in #52.
Acceptance criteria
Finding #3 of the 2026-08 architecture review. Partially addressed in #52; the remaining half is breaking.
Problem
MutexInterface.releaseLock()returnsMaybePromise<boolean>, wherefalsemeans the release failed.Statemachine.releaseLock()discards that boolean and is declaredPromise<void>(src/interfaces/StatemachineInterface.ts:31).The engine's automatic release path was fixed in #52: a
falsereturn is now treated exactly like a thrown release error, reported toonReleaseError, and raised asLockCanNotBeReleasedError. But callers doing manual lock management — the documented batch pattern indocs/mutex.md,autoreleaseLock: false— callsm.releaseLock()directly. For them, #52 could only add the diagnostic hook; the return value is still dropped, so the straightforward reading ofis still wrong when the underlying adapter reports failure. Detecting it requires either wiring
onReleaseErroror interrogating the mutex, neither of which is discoverable from the signature.Why it was deferred
Widening
Promise<void>toPromise<boolean>is source-compatible for callers who ignore the result, but it breaks anyone implementingStatemachineInterface— theirreleaseLock(): Promise<void>no longer satisfies the interface. That is a major-version change.Proposed direction
Change both the class and
StatemachineInterfacetoreleaseLock(): Promise<boolean>, returning what the mutex reported. ThrowingLockCanNotBeReleasedErrorinstead is the alternative; it is more consistent with the automatic path, but it turns a previously silent situation into an exception in existing user code, so the boolean is the gentler option. Pick one and document it indocs/mutex.mdalongside the release-failure semantics added in #52.Acceptance criteria
releaseLock()communicates failure through its own signature, not only throughonReleaseError.StatemachineInterfaceupdated to match.docs/mutex.mdanddocs/core.mdmethod tables updated.docs/migration/.