Skip to content

agent: listener-less (cadence-only) mode (F35)#175

Merged
mbertschler merged 4 commits into
claude/issue-162-bootstrap-helpersfrom
claude/issue-162-listener-less
Jul 24, 2026
Merged

agent: listener-less (cadence-only) mode (F35)#175
mbertschler merged 4 commits into
claude/issue-162-bootstrap-helpersfrom
claude/issue-162-listener-less

Conversation

@mbertschler

Copy link
Copy Markdown
Owner

Summary

Completes the last scoped item of #162 — friction F35: "cadence-only machines must run the full agent listener just to get the scheduler." The HTTP listener is now optional. When [agent] listen is absent/empty the agent runs its background schedulers (index/sync/audit cadences) without binding an HTTP server; when listen is set, behaviour is exactly as before (scheduler + listener, token required). In the reference setup this lets the roaming laptop drop its dummy listen = 127.0.0.1:8443 (and the now-pointless auth token) — the wart F35 named.

Changes

  • config (config/agent.go) — listen is no longer required; a bearer token is required only when a listener is configured (a listener-less agent has no HTTP surface to authenticate). A bare [agent] block is valid.
  • agent (agent/agent.go, agent/serve.go) — validateConfig relaxed to match; new Server.RunSchedulers(ctx) runs the scan + cadence loops with the same start-gating and shutdown discipline as Serve, minus the HTTP server.
  • cmd (cmd/squirrel/agent.go) — runAgent dispatches to RunSchedulers when listen is empty, and fails fast if a listener-less agent has no cadence and no scan_interval (a silently-idle agent is degradation, not a valid config). The marker/init boundary is untouched — the agent still never escalates (never --init).
  • docsreference-setup.md laptop config drops the dummy listen/token; the agent guide and configuration reference document listener-less operation.

Design decision to verify

A listener-less agent with no cadences and no scan_interval is refused at startup (listener-less agent has nothing to run: …) rather than allowed to idle. Rationale: silent-idle is exactly the kind of quiet degradation ux-principle 4 warns against. A listener-ful agent with no cadences stays valid (a receive-only node like the htpc legitimately just serves). Please confirm the fail-fast is the behaviour you want.

Testing

  • go vet ./..., go test ./..., golangci-lint run (v2.12.2 / go1.26.1), gofmt -l — all clean.
  • New tests: config listener-less load (bare block + token-without-listener); agent.New listener-less accept + rejection-table update; Server.RunSchedulers returns cleanly on cancel; CLI end-to-end (scheduler-only banner, no listener bound, clean shutdown) and the no-work fail-fast.

Scope

Closes #162 — this completes the deferred F35 item, the last box on the issue.

Stacked on #162's bootstrap PR #171; base is claude/issue-162-bootstrap-helpers and retargets to main once #171 merges. Expect a merge reconciliation with the in-flight agent-startup changes on #154 (scheduler start gate) and #157 (orphan-run reaping) — implemented cleanly against #162 only.

🤖 Generated with Claude Code

https://claude.ai/code/session_014Skm2Qm6wWUB7dD4CpPTz7


Generated by Claude Code

Make the HTTP listener optional. When `[agent] listen` is absent/empty the
agent runs its background schedulers (index/sync/audit cadences) WITHOUT
binding an HTTP server — the mode for cadence-only machines (a roaming
laptop that pushes to a hub and never receives peer syncs). When `listen`
is set, behaviour is unchanged: scheduler + HTTP server, token required.

- config: `listen` no longer required; a bearer token is required only
  when a listener is configured (nothing to authenticate otherwise).
- agent: validateConfig relaxed accordingly; new Server.RunSchedulers runs
  the scan + cadence loops with the same shutdown discipline as Serve but
  no listener.
- cmd: runAgent dispatches to RunSchedulers when listen is empty, and
  fails fast if a listener-less agent has no cadence and no scan (silent
  idling is degradation, not a valid config); the marker/init boundary is
  untouched — the agent still never escalates.
- docs: reference-setup laptop drops the dummy listen + token; agent guide
  and configuration reference describe listener-less operation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Skm2Qm6wWUB7dD4CpPTz7
Copilot AI review requested due to automatic review settings July 24, 2026 11:36
Strike the listener-less friction entry now that the cadence-only agent
mode lands, per the friction log's PR-reference convention.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Skm2Qm6wWUB7dD4CpPTz7

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables a listener-less (“cadence-only”) agent mode so machines can run the agent’s scan/scheduler loops without binding an HTTP server, removing the need for a dummy listen address and auth token on machines that never receive peer syncs.

Changes:

  • Relaxed agent config validation so listen is optional and auth.token is only required when an HTTP listener is configured.
  • Added a scheduler-only execution path (Server.RunSchedulers) and CLI dispatch/fail-fast behavior for listener-less agents that would otherwise be idle.
  • Updated documentation and reference setup to describe and use listener-less operation.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
docs/src/content/docs/reference/configuration.md Documents optional listen and listener-less scheduler-only behavior.
docs/src/content/docs/guides/agent.md Adds a dedicated listener-less section with example config and behavior notes.
design/reference-setup.md Updates reference laptop config to omit dummy listen/token.
config/config_test.go Updates config tests to accept listener-less [agent] blocks.
config/agent.go Makes listen optional and relaxes auth requirements for listener-less mode.
cmd/squirrel/agent.go Dispatches to scheduler-only run path when listen is empty; adds “no work” fail-fast and banner.
cmd/squirrel/agent_listenerless_test.go Adds end-to-end CLI tests for listener-less scheduling and fail-fast behavior.
agent/serve.go Adds RunSchedulers to run scan/scheduler loops without an HTTP server.
agent/agent.go Relaxes server config validation: token required only when listen is set.
agent/agent_test.go Adds tests for listener-less server construction and RunSchedulers cancellation behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread config/agent.go
Comment thread cmd/squirrel/agent.go
claude added 2 commits July 24, 2026 11:42
- config/agent.go: the "token required" error now states the requirement
  is conditional on a listener — "auth.token is required when [agent]
  listen is set (... omit listen for a listener-less, scheduler-only
  agent)" — instead of the unconditional "no agent without
  authentication", which read as wrong now that listener-less mode exists.
- cmd/squirrel/agent.go: the listener-less "nothing to run" fail-fast now
  lists hook.interval alongside sync_every/index_every/scan_interval, so
  the remediation matches the real scheduling rules (agentHasWork and the
  scheduler both count an interval hook as work).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Skm2Qm6wWUB7dD4CpPTz7
F35 made Listen optional and Token conditional on Listen, but three
doc comments on the public Config/Agent structs still declared them
unconditionally required, contradicting the relaxed validateConfig /
resolveAgentAuth. Correct the comments so the documented contract
matches the validated one. Doc-only; no behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Skm2Qm6wWUB7dD4CpPTz7
@mbertschler
mbertschler merged commit efce410 into claude/issue-162-bootstrap-helpers Jul 24, 2026
3 checks passed
@mbertschler
mbertschler deleted the claude/issue-162-listener-less branch July 24, 2026 21:28
mbertschler pushed a commit that referenced this pull request Jul 24, 2026
main now carries #171 and the listener-less agent mode (#175), which is
where this branch's two new cadences had to be reconciled.

Textual conflicts:
- store/runs_audit.go, agent/agent.go: both sides appended at the same
  spot — keep this branch's pull-durability transition and its
  VerifyRunner/DurabilityPuller types alongside main's reset-destination
  transition; main rewrote the Config doc comment for listener-less mode,
  so its version of that comment stands.
- docs reference: both sides added to the [agent] section. Kept main's
  listener-less paragraph and this branch's key tables, and corrected the
  table to match — `listen` is no longer required, and `auth.token` is
  required only alongside it. Left as-is the two would have contradicted
  each other in adjacent paragraphs.

Semantic conflict: main's listener-less start gate (cmd's agentHasWork)
duplicated the scheduler's volume-cadence logic and predated this branch's
verify_every / pull_durability_every, so it would have refused to start a
listener-less agent whose only work is one of those cadences — exactly the
receive-only htpc in design/reference-setup.md that F33 exists to serve.
Replaced the duplicate with an exported agent.ScheduledWorkInConfig, built
on the same resolvers the scheduler's own anyScheduledWork gate uses, so
the two cannot drift again; the refusal message now names every cadence.
Covered by a table test including the receive-only pull-only config.

go vet, go test ./..., and gofmt are clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SQUzRmQHqbP2kV9kFrbBHL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants