From 1330ad9943ff8b6be08af916730008650a117fb6 Mon Sep 17 00:00:00 2001 From: kami Date: Sat, 29 Aug 2026 20:41:36 +0400 Subject: [PATCH] Stop reporting a blocker the task is no longer blocked on F67. TaskCorrected cleared the standing question when a task resumed and left Blocker and BlockReason in place, so task 29 ran through implement, review and submission still reporting block_reason plan_mismatch. Every surface that reads the projection rather than the event log showed a stop that had already been answered. The blocker has the same lifetime as the question beside it, and is now cleared with it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CVbaKucEYBjMqVeUgJUsc1 --- internal/operations/planmismatch_test.go | 4 ++++ internal/store/store.go | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/internal/operations/planmismatch_test.go b/internal/operations/planmismatch_test.go index 8e5d658..c56d770 100644 --- a/internal/operations/planmismatch_test.go +++ b/internal/operations/planmismatch_test.go @@ -225,6 +225,10 @@ func TestHumanAnswerResumesTheSamePlanWithoutResealing(t *testing.T) { if after.State != domain.StateQueued { t.Fatalf("state = %s after the human answered, want queued", after.State) } + // F67: the stop is over, so the projection must not keep reporting it. + if after.BlockReason != "" || after.Blocker != "" { + t.Fatalf("a resumed task still reports its blocker: %q %q", after.BlockReason, after.Blocker) + } if after.PlanRef != planRef { t.Fatal("answering the question replaced the plan") } diff --git a/internal/store/store.go b/internal/store/store.go index 8d886bf..f537ef2 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -588,8 +588,14 @@ func (s *Store) apply(e domain.Event) error { // A question only stands while the task is blocked on it. Afterwards the // answer is an ordinary standing decision and the log still holds the // question, so keeping it on the task would put it in every later context. + // + // The blocker itself has the same lifetime and was outliving it (F67). A + // task resumed from a plan mismatch kept reporting block_reason + // plan_mismatch while it ran, so every surface reading the projection + // showed a stop that had already been answered. if t.State != domain.StateBlocked { t.DecisionRequest = nil + t.Blocker, t.BlockReason = "", "" } if phase, ok := p["lifecycle_phase"].(string); ok && phase != "" { t.LifecyclePhase = phase