Make Complete atomic and guard zero deadline
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -71,12 +71,15 @@ func (c *Controller) Deadline() time.Time {
|
||||
func (c *Controller) stateLocked() State {
|
||||
st := State{RuntimeState: c.runtimeState}
|
||||
if c.commitment != nil {
|
||||
st.Commitment = &CommitmentView{
|
||||
view := &CommitmentView{
|
||||
NextAction: c.commitment.NextAction,
|
||||
SuccessCondition: c.commitment.SuccessCondition,
|
||||
TimeboxSecs: c.commitment.TimeboxSecs,
|
||||
DeadlineUnixSecs: c.deadline.Unix(),
|
||||
}
|
||||
if !c.deadline.IsZero() {
|
||||
view.DeadlineUnixSecs = c.deadline.Unix()
|
||||
}
|
||||
st.Commitment = view
|
||||
}
|
||||
return st
|
||||
}
|
||||
@@ -124,7 +127,9 @@ func (c *Controller) StartManualCommitment(nextAction, successCondition string,
|
||||
return c.persistLocked()
|
||||
}
|
||||
|
||||
// Complete moves Active -> Review and marks the commitment completed.
|
||||
// Complete moves Active -> Review and marks the commitment completed. Both
|
||||
// transitions are computed before any field is mutated, so a failure leaves
|
||||
// state unchanged.
|
||||
func (c *Controller) Complete() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
@@ -132,12 +137,14 @@ func (c *Controller) Complete() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.runtimeState = next
|
||||
if c.commitment != nil {
|
||||
if s, e := statemachine.TransitionCommitment(c.commitment.State, statemachine.Complete); e == nil {
|
||||
c.commitment.State = s
|
||||
completed, err := statemachine.TransitionCommitment(c.commitment.State, statemachine.Complete)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.commitment.State = completed
|
||||
}
|
||||
c.runtimeState = next
|
||||
return c.persistLocked()
|
||||
}
|
||||
|
||||
|
||||
@@ -85,3 +85,23 @@ func TestStateRestoresFromSnapshot(t *testing.T) {
|
||||
t.Fatalf("restored commitment missing: %+v", st.Commitment)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRestoredCommitmentKeepsDeadline(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "state.json")
|
||||
first, err := New(path)
|
||||
if err != nil {
|
||||
t.Fatalf("new: %v", err)
|
||||
}
|
||||
_ = first.EnterPlanning()
|
||||
_ = first.StartManualCommitment("Keep deadline", "done", 25*time.Minute)
|
||||
want := first.State().Commitment.DeadlineUnixSecs
|
||||
|
||||
second, err := New(path)
|
||||
if err != nil {
|
||||
t.Fatalf("reopen: %v", err)
|
||||
}
|
||||
got := second.State().Commitment
|
||||
if got == nil || got.DeadlineUnixSecs != want {
|
||||
t.Fatalf("restored deadline: got %+v want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user