Tell the agent the artifact shape, and tell it when the shape is wrong

Two halves of the same failure, live on run 5.

F38: the phase brief named .orchestra/research.json and described its contents
in prose, never its schema. The agent guessed dead_ends as strings where the
decoder wants {tried, why_failed} objects. The brief now carries the shape, and
a test decodes each documented shape with the same function the worker uses, so
a struct change that is not mirrored fails the build.

F39: the local artifact check refused the request through recordError alone.
answerRefusedPhase only ran on a coordinator 409, so a decode failure told the
agent nothing. The session sat at a boundary rewriting nothing, which is the
silent-loop shape the comment above that block warns about, reached by the one
path with no delivery. Both local refusals now reach the agent.

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:58:36 +04:00
parent 87a3a6e2e4
commit 0bd86e28c6
3 changed files with 76 additions and 8 deletions
+34
View File
@@ -7,6 +7,7 @@ import (
"orchestra/internal/continuity"
"orchestra/internal/domain"
"orchestra/internal/workphase"
)
func input() Input {
@@ -351,3 +352,36 @@ func TestPhaseBriefNamesTheArtifactToSeal(t *testing.T) {
}
}
}
// TestPhaseSealSchemasDecode guards F38. The brief tells the agent to seal an
// artifact; until this existed it did not say what shape. Run 5 guessed
// dead_ends as strings where the decoder wants objects, and every phase
// request was refused for a field nobody had described.
//
// Each documented shape is decoded by the same function the worker uses, so a
// struct change that is not mirrored in the brief fails here instead of in a
// live run.
func TestPhaseSealSchemasDecode(t *testing.T) {
for phase, schema := range phaseSealSchema {
if schema == "" {
t.Fatalf("%s has a seal file but no documented shape", phase)
}
var decErr error
switch phase {
case domain.WorkPhaseResearch:
_, decErr = workphase.DecodeResearch([]byte(schema))
case domain.WorkPhasePlan:
_, decErr = workphase.DecodePlan([]byte(schema))
default:
t.Fatalf("%s has a documented shape with nothing to decode it", phase)
}
if decErr != nil && !strings.Contains(decErr.Error(), "empty") && !strings.Contains(decErr.Error(), "required") && !strings.Contains(decErr.Error(), "must") {
t.Fatalf("%s brief shape does not match the decoder: %v", phase, decErr)
}
}
for phase := range phaseSealFile {
if phaseSealSchema[phase] == "" {
t.Fatalf("%s names a seal file but the brief never states its shape", phase)
}
}
}