Documentation
¶
Overview ¶
Package smoke runs end-to-end smoke tests against a reef-core instance.
This file implements the full pod lifecycle smoke test: spawn a lobster pod, observe live WebSocket events (stream, tool_call, cost), reconcile the cost against the REST budget delta, kill the pod, and confirm a terminal status.
Package smoke runs a non-TUI end-to-end smoke test against a reef-core instance. It exercises the full Phase 3 custody chain: structured_bundle → openbrain_snapshot → capture_commitment → custody.
Preconditions:
- reef-core running on the target URL (default http://localhost:4000)
- A valid session token (get one via: cd reef-core && mix reef_core.dev_session -q)
- opencode on reef-core's PATH
- ANTHROPIC_API_KEY set on reef-core's side (not konareef's)
Flow:
- POST /api/agents with structured memory bundle + capture-thought task
- Server validates session, stores bundle, emits structured_bundle proof, takes Open Brain snapshot, spawns opencode, runs LLM call, captures thought, emits capture_commitment + custody proofs
- Client polls GET /api/proofs?agent_id=... until a custody proof appears or the timeout hits
- Print chain summary + result
Index ¶
- Variables
- func Run(cfg Config) error
- func RunLifecycle(cfg Config) error
- func TokenFromEnv() string
- type Config
- type Event
- type EventCollector
- func (collector *EventCollector) All() []Event
- func (collector *EventCollector) Close() error
- func (collector *EventCollector) Expect(topic, eventType, agentID string, timeout time.Duration) (Event, error)
- func (collector *EventCollector) JoinAgent(agentID string) error
- func (collector *EventCollector) Start(ctx context.Context) error
- func (collector *EventCollector) SumCost() int64
Constants ¶
This section is empty.
Variables ¶
var CannedBundle = map[string]string{
"projects/goals.md": `# Goals
Prove that konareef can spawn a pod on reef-core and receive a valid
custody proof chain back.
`,
"projects/decisions.md": `# Decisions
- 2026-04-15: First end-to-end konareef → reef-core smoke test
- 2026-04-15: TUI spawn flow deferred; smoke is a separate subcommand
`,
"preferences/coding-style.md": `# Coding style
- Go: tab indent, gofmt, no comments on obvious code
- Elixir: match CLAUDE.md conventions
`,
}
CannedBundle is a canned structured memory bundle for the smoke test. Three small files mimicking what an orchestrator would upload before spawning a pod.
Functions ¶
func Run ¶
Run executes the smoke test. Returns nil on success (custody proof observed, chain of length 3 or 4 verified), or an error on any failure.
func RunLifecycle ¶
RunLifecycle executes the full pod lifecycle smoke test against reef-core.
The test proceeds through ten steps:
- Preflight: confirm reef-core is reachable via ListAgents.
- Snapshot the pre-spawn budget via GetBudget.
- Spawn a lobster pod with cfg.Task (defaulting to a canned capture-thought task).
- Open a WebSocket collector, join pod:events and agent:<id>.
- Expect a "stream" wire event on the per-agent channel within cfg.Timeout.
- Expect a "tool_call" wire event within cfg.Timeout (optional; warns if absent).
- Expect an "agent_cost" event on pod:events within cfg.Timeout.
- Reconcile WS-reported cost against the REST budget delta (5% tolerance).
- Kill the agent via KillAgent and expect agent_exited on pod:events.
- Confirm the agent status is terminal via ListAgents.
cfg must have a non-empty SessionToken. cfg.Task may be empty, in which case the default capture-thought task from Default() is used.
Returns nil on success, or a descriptive wrapped error on any failure.
func TokenFromEnv ¶
func TokenFromEnv() string
TokenFromEnv reads the session token from KONAREEF_TOKEN, allowing shells to `export KONAREEF_TOKEN=$(cd ../reef-core && mix reef_core.dev_session -q)`.
Types ¶
type Config ¶
type Config struct {
BaseURL string
SessionToken string
Task string
Timeout time.Duration
PollInterval time.Duration
MemoryRoundtrip bool
// PodAttestation, when non-nil, attaches publisher signing
// metadata to the spawn request so the resulting structured_bundle
// commitment's pod_hash / publisher_signature columns are populated.
// The caller supplies this from `install.LoadCachedAttestation`
// when spawning a previously installed signed pod (P0.3R).
PodAttestation *api.PodAttestation
}
Config configures a smoke test run.
MemoryRoundtrip is opt-in (default false) because it requires a reef-core build that includes the Phase 1.5 memory subsystem (the MemoryHook teardown wiring + GET /api/memory/entries). When false, the lifecycle smoke skips the memory verification step entirely so it can still pass against older reef-core deployments.
type Event ¶
type Event struct {
// Topic is the Phoenix channel topic, e.g. "pod:events" or "agent:<id>".
Topic string
// EventType is the Phoenix event name, e.g. "agent_started", "stream", "cost".
EventType string
// AgentID is extracted from the topic when the topic is "agent:<id>",
// or from payload["agent_id"] for lifecycle events on "pod:events".
AgentID string
// Payload is the raw parsed payload map from the Phoenix message.
Payload map[string]any
// ReceivedAt is the wall-clock time at which the event was buffered.
ReceivedAt time.Time
}
Event is a single WebSocket event captured from reef-core.
type EventCollector ¶
type EventCollector struct {
// contains filtered or unexported fields
}
EventCollector subscribes to reef-core's Phoenix Channel WebSocket and buffers every inbound event for test assertions.
func NewEventCollector ¶
func NewEventCollector(serverURL, token string) *EventCollector
NewEventCollector creates a collector pointed at the reef-core HTTP base URL (e.g. "http://localhost:4000"). The token is a base64-encoded reef-core session token. Does not connect; call Start to open the connection.
func (*EventCollector) All ¶
func (collector *EventCollector) All() []Event
All returns a snapshot copy of every event received so far. Safe to call from any goroutine; the returned slice is independent of the internal buffer.
func (*EventCollector) Close ¶
func (collector *EventCollector) Close() error
Close shuts the WebSocket connection down gracefully and cancels the read-loop and heartbeat goroutines. Idempotent — safe to call multiple times.
func (*EventCollector) Expect ¶
func (collector *EventCollector) Expect(topic, eventType, agentID string, timeout time.Duration) (Event, error)
Expect blocks until an event matching topic, eventType, and agentID arrives, or the timeout expires. Events that arrived before Expect was called are also eligible — the collector buffers everything since Start. Pass an empty string for topic, eventType, or agentID to match any value in that field. Returns the matched Event and nil on success, or a zero Event and an error on timeout.
func (*EventCollector) JoinAgent ¶
func (collector *EventCollector) JoinAgent(agentID string) error
JoinAgent also joins the per-agent channel "agent:<agentID>". Safe to call after Start. Returns once the join is confirmed or the context stored at Start time is cancelled.
func (*EventCollector) Start ¶
func (collector *EventCollector) Start(ctx context.Context) error
Start opens the WebSocket connection and joins "pod:events". It blocks until the join is confirmed or the context is cancelled. Once confirmed, it spawns a background read-loop goroutine that exits when ctx is cancelled or the connection drops. Returns an error if the dial or join fails.
func (*EventCollector) SumCost ¶
func (collector *EventCollector) SumCost() int64
SumCost returns the sum of "sats" values across every "agent_cost" event seen on the "pod:events" topic — the canonical pod-wide feed BudgetModel consumes. Use this to reconcile against REST budget deltas in lifecycle tests.
reef-core broadcasts each :agent_cost PubSub event to BOTH "pod:events" (this feed) and the per-agent "agent:<id>" topic (where it surfaces as a "cost" wire event consumed by the Inspector). Counting both feeds would double-count every spend; a single source is the right reconciliation target. The smoke test asserts both feeds independently to keep coverage of the per-agent path.