fix(herdr): B5 — replace invented pane.kill/release/rotation_signal with real methods
Verified against a live herdr instance (192.168.1.105:9245) that pane.kill,
pane.release, and pane.rotation_signal never existed in the protocol, as
AUDIT.md's B5 suspected. Real method list captured in deploy/herdr-schema.json.
- Kill now calls the real pane.close({pane_id}).
- RotationSignal interface/method/call-site deleted; no real equivalent exists.
- Release now refuses loudly instead of calling a nonexistent method — the
real pane.release_agent can't return a handoff_ref either way (herdr
doesn't write handoffs, the agent does), so a real fix needs Phase 4
handoff production first.
Also documents Phase 0 findings in AUDIT.md/progress.md, and adds
CLAUDE.md/AGENTS.md with project-specific knowledge (herdr protocol facts,
deployment topology, a currently-stuck live task, the federation fork) for
future sessions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1rkJ2hBMybnJctPbcy4tT
This commit is contained in:
+12
-18
@@ -26,9 +26,6 @@ type WorktreeCreator interface {
|
||||
type TurnBoundary interface {
|
||||
AtTurnBoundary(context.Context, Session) (bool, error)
|
||||
}
|
||||
type RotationSignal interface {
|
||||
RotationSignal(context.Context, Session) (string, error)
|
||||
}
|
||||
type PaneExit interface {
|
||||
PaneExited(context.Context, Session) (bool, error)
|
||||
}
|
||||
@@ -78,15 +75,21 @@ func (a CLIAdapter) Lease(ctx context.Context, task, worktree string) (Session,
|
||||
func (a CLIAdapter) Bootstrap(ctx context.Context, s Session, ref string) error {
|
||||
return a.Client.Prompt(ctx, s.PaneID, fmt.Sprintf("Read handoff %s, validate the anchor and TASK.md, then continue.", ref), time.Minute)
|
||||
}
|
||||
// Release previously called the invented "pane.release" method expecting a
|
||||
// handoff_ref back. Neither exists in the real protocol (confirmed against a
|
||||
// live herdr instance, AUDIT.md Phase 0): the real method is
|
||||
// pane.release_agent(pane_id, source, agent), which only releases herdr's
|
||||
// claim on the agent session — it cannot return a handoff_ref, because herdr
|
||||
// does not write handoffs, the agent does (§6.1). Wiring this correctly needs
|
||||
// the Phase 4 handoff-production path (agent writes handoff, stop hook
|
||||
// uploads it to CAS, plane validates and mints the ref) before Release has
|
||||
// anything real to return. Refusing loudly until then rather than calling a
|
||||
// method that doesn't exist.
|
||||
func (a CLIAdapter) Release(ctx context.Context, s Session) (string, error) {
|
||||
var r struct {
|
||||
Ref string `json:"handoff_ref"`
|
||||
}
|
||||
e := a.Client.Call(ctx, "pane.release", s, &r)
|
||||
return r.Ref, e
|
||||
return "", fmt.Errorf("adapter: Release not implemented — pane.release is not a real herdr method and handoff production (AUDIT.md Phase 4) is not wired yet")
|
||||
}
|
||||
func (a CLIAdapter) Kill(ctx context.Context, s Session) error {
|
||||
return a.Client.Call(ctx, "pane.kill", s, nil)
|
||||
return a.Client.Call(ctx, "pane.close", map[string]any{"pane_id": s.PaneID}, nil)
|
||||
}
|
||||
func (a CLIAdapter) AtTurnBoundary(ctx context.Context, s Session) (bool, error) {
|
||||
status, err := a.AgentStatus(ctx, s)
|
||||
@@ -179,15 +182,6 @@ func statusFromAgentResult(v any) string {
|
||||
|
||||
var _ = json.RawMessage{}
|
||||
|
||||
func (a CLIAdapter) RotationSignal(ctx context.Context, s Session) (string, error) {
|
||||
var r struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
if err := a.Client.Call(ctx, "pane.rotation_signal", s, &r); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return r.Reason, nil
|
||||
}
|
||||
// Occupancy reads the harness's own session state — never the herdr pane id,
|
||||
// which ClaudeUsage/CodexUsage/OpenCodeUsage cannot open (spec §5.2.1: "the
|
||||
// whole rotation system rests on this number"). A session file that cannot
|
||||
|
||||
@@ -451,13 +451,8 @@ func (c *Coordinator) rotate(ctx context.Context, hard float64) {
|
||||
continue
|
||||
}
|
||||
reason := "threshold"
|
||||
if signal, ok := a.(herdr.RotationSignal); ok {
|
||||
if r, signalErr := signal.RotationSignal(ctx, session); signalErr == nil && r != "" {
|
||||
reason = r
|
||||
}
|
||||
}
|
||||
occupancy, err := a.Occupancy(session)
|
||||
if err != nil || (occupancy < hard && reason == "threshold") {
|
||||
if err != nil || occupancy < hard {
|
||||
continue
|
||||
}
|
||||
// Face B is treated as required, not best-effort (spec §5.2/§5.3):
|
||||
|
||||
Reference in New Issue
Block a user