Let the validator accept the reason the adapter produces

handoffReason has emitted "phase_changed" since phase rotations landed. The
continuity validator's reason list was never extended, so every phase rotation
built a handoff it then refused as "invalid handoff meta".

Live on run 5: with F31 clearing the parse ahead of it, the release reached
this and stopped at phase "prepared" with
"adapter: upload handoff: invalid handoff meta".

The test asserts the property rather than the constant: every reason the
adapter can produce must survive Validate, including its fallback.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011xsXyr5J1RACo71YeKG3Pu
This commit is contained in:
2026-08-28 01:38:58 +04:00
parent 92f32d6fea
commit b9ca365b9a
2 changed files with 25 additions and 1 deletions
+19
View File
@@ -315,3 +315,22 @@ func TestReleaseRequiresCAS(t *testing.T) {
t.Fatal("expected error when CAS is nil")
}
}
// TestEveryReasonTheAdapterProducesIsAcceptedByTheValidator guards F36. The
// adapter gained "phase_changed" when phase rotations landed and the
// validator's vocabulary did not, so every phase rotation built a handoff that
// was then refused as "invalid handoff meta". A live run reached exactly this
// point after F31 unblocked the parse ahead of it.
func TestEveryReasonTheAdapterProducesIsAcceptedByTheValidator(t *testing.T) {
for _, reason := range []string{"threshold", "milestone", "thrash", "manual", "reconcile_failure", "phase_changed", "something the adapter does not know"} {
produced := handoffReason(Session{HandoffReason: reason})
h := continuity.Handoff{
Meta: continuity.Meta{ID: "agent-1", Reason: produced},
Anchor: continuity.Anchor{GitSHA: "0000000000000000000000000000000000000000", Branch: "main"},
Action: "write the research note",
}
if err := h.Validate(); err != nil {
t.Fatalf("adapter produces reason %q for %q, validator refuses it: %v", produced, reason, err)
}
}
}