Files
kyleandClaude Fable 5.1 74b8af4ce0 AGENTS.md: a refused tool call is not a reason to end the turn; v2 plan (props, ctx guard, wake, identity, wiring) as acceptance tests; v1.1 run note
v2 given tests compiled against a panic-only skeleton (go vet clean); no reference
implementation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 08:37:38 -07:00

2.7 KiB
Raw Permalink Blame History

v2 task 03: wake-on-LAN

Branch: v2 (run git switch v2; git status --short must be empty, otherwise stop) Commit subject: Add the wake package: magic packets and a waiter

Goal

A pure package. wake.MagicPacket builds the 102-byte wake-on-LAN frame, wake.Send puts it on the wire as UDP, and wake.Waker wakes a named host at most once per wait window and waits for the health table to report it healthy. Task 05 uses it when a route has no healthy host.

Context

A magic packet is six 0xff bytes followed by the target MAC sixteen times, sent as a UDP datagram to the LAN broadcast address (port 9 by convention). The sleeping Mac (titan) has wake-on-magic-packet enabled; it takes 20–40 s to be reachable. Waking twice inside that window is harmless but pointless, so the waker remembers when it last sent.

Files

  • Copy: internal/wake/wake_test.go
  • Create: internal/wake/wake.go
  • Modify: docs/implementer-log.md

Interfaces

package wake

type Target struct {
    MAC, Broadcast string   // "aa:bb:cc:dd:ee:ff" (also "-" separated, any case); "host:port"
    Wait           time.Duration
}
type Health interface{ Healthy(name string) bool }

func MagicPacket(mac string) ([]byte, error)     // net.ParseMAC; must be 6 bytes; 102-byte frame
func Send(mac, broadcast string) error           // one UDP datagram via net.DialUDP("udp4", …); errors from parse/resolve/write
type Waker struct { /* private: targets, health, mutex, last-sent per host, poll interval (default 1s) */ }
func New(targets map[string]Target, h Health) *Waker
func (w *Waker) PollEvery(d time.Duration)       // test hook; production keeps the 1 s default
// Wake returns true as soon as h.Healthy(host) is true, false if host is unknown, if Wait passes,
// or if ctx ends first. It sends the packet only if none was sent for host in the last Wait.
func (w *Waker) Wake(ctx context.Context, host string) bool

Rules the tests check: packet layout; separators; errors for bad MACs and unresolvable addresses; one packet per window; return within about Wait when the host never comes up; early return on a cancelled context; false for an unknown host without sending anything. Never panic; safe for concurrent Wake calls on different hosts.

Steps

  • 1. git switch v2; mkdir -p internal/wake; copy the test.
  • 2. See it fail (compile). 3. Write wake.go. gofmt -w internal/wake/.
  • 4. go test -race -count=3 ./internal/wake/ → ok (timing tests; three runs).
  • 5. make gate. 6. Row v2/03-wake; commit.
git add internal/wake docs/implementer-log.md
git commit

Done when

  • -race -count=3 passes; gate ok; the copied test is byte-identical.