Reconcile docs with reality; fix module graph, token compare, health #1
@@ -151,6 +151,12 @@ const maxAuthoredAction = 2*maxAuthoredLine + len(" — ")
|
||||
var circularAction = regexp.MustCompile(`(?i)handoff|report\.md|^continue the task`)
|
||||
var circularCommand = regexp.MustCompile(`(?i)\.orchestra-handoff|handoff-report|report\.md`)
|
||||
|
||||
// IsCircularCommand reports whether Validate would reject this as a command
|
||||
// that points at a handoff. Producers need the same answer before they build a
|
||||
// handoff, so the rule lives in one place rather than being restated as a
|
||||
// second regexp that can drift.
|
||||
func IsCircularCommand(s string) bool { return circularCommand.MatchString(s) }
|
||||
|
||||
func (h Handoff) Validate() error {
|
||||
if strings.TrimSpace(h.Meta.ID) == "" || !reasons[h.Meta.Reason] || h.Meta.RotationIndex < 0 {
|
||||
return errors.New("invalid handoff meta")
|
||||
|
||||
@@ -538,9 +538,19 @@ func (a CLIAdapter) lastObservedCommand(s Session) string {
|
||||
return ""
|
||||
}
|
||||
for i := len(calls) - 1; i >= 0; i-- {
|
||||
if calls[i].Kind == "command" {
|
||||
return calls[i].Key
|
||||
if calls[i].Kind != "command" {
|
||||
continue
|
||||
}
|
||||
// Skip the write Orchestra itself asked for. The handoff prompt tells
|
||||
// the agent to write HandoffReportFile and stop, so that write is
|
||||
// almost always the last command in the pane. Carrying it into
|
||||
// Handoff.Command made every rotation fail its own circularity check
|
||||
// with "must not point to a handoff or report" (F37). The successor
|
||||
// wants the last command that was real work.
|
||||
if continuity.IsCircularCommand(calls[i].Key) {
|
||||
continue
|
||||
}
|
||||
return calls[i].Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -334,3 +334,34 @@ func TestEveryReasonTheAdapterProducesIsAcceptedByTheValidator(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestLastObservedCommandSkipsOrchestrasOwnHandoffWrite guards F37. The handoff
|
||||
// prompt tells the agent to write HandoffReportFile and stop, so that write is
|
||||
// almost always the last command in the pane. Carrying it into Handoff.Command
|
||||
// made every rotation fail Validate's own circularity check. Live on run 5:
|
||||
// "adapter: upload handoff: invalid handoff command: must not point to a
|
||||
// handoff or report".
|
||||
func TestLastObservedCommandSkipsOrchestrasOwnHandoffWrite(t *testing.T) {
|
||||
for _, last := range []string{
|
||||
"cat > " + HandoffReportFile,
|
||||
"vim .orchestra-handoff.json",
|
||||
"less handoff-report.md",
|
||||
} {
|
||||
if !continuity.IsCircularCommand(last) {
|
||||
t.Fatalf("%q is not recognised as circular, so this test proves nothing", last)
|
||||
}
|
||||
h := continuity.Handoff{
|
||||
Meta: continuity.Meta{ID: "agent-1", Reason: "phase_changed"},
|
||||
Anchor: continuity.Anchor{GitSHA: "0000000000000000000000000000000000000000", Branch: "main"},
|
||||
Action: "write the research note",
|
||||
Command: "go test ./...",
|
||||
}
|
||||
if err := h.Validate(); err != nil {
|
||||
t.Fatalf("a real command was rejected: %v", err)
|
||||
}
|
||||
h.Command = last
|
||||
if err := h.Validate(); err == nil {
|
||||
t.Fatalf("%q was accepted, so skipping it in the producer is not what keeps rotations alive", last)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user