Field Body Daemon
Purpose
A service that only runs when a human clicks something is not persistent — it is
a program someone remembers to start. Making a service genuinely long-lived
requires three parts that are usually built ad hoc and badly: bootstrapping
itself without double-starting, watching itself without lying about its health,
and remembering across restarts without replaying its whole history.
This skill supplies all three as configurable, dependency-light scripts, plus
the credential-isolation discipline required to ship such a system to anyone
other than its author.
When to Use
- Standing up a local backend, agent runtime, or desktop companion that should
auto-start and stay up. - Adding a watchdog to an existing service that currently has none, or has one
that reports optimistically. - Giving a long-running process continuity across restarts.
- Preparing such a system for distribution, where hardcoded interpreter paths
and API keys must be stripped.
Architecture
Three separable layers. Adopt them independently; they do not require each other.
bootstrap -> is it already up? reuse. else spawn, wait for ready, verify.
guard -> every N seconds: probe, resolve tri-state, score, log.
memory -> each cycle: diff against previous snapshot, persist the delta.
Layer 1 — Bootstrap
The dominant bootstrap bug is double-starting: a second instance spawns, fails
to bind the port, dies silently, and the caller reports success because a
service answers. Always probe the port before spawning.
Correct sequence:
- Probe the health endpoint. If it answers, reuse and skip spawning.
- Otherwise spawn, with the working directory set to the module's own directory
— import paths usually depend on it. - Poll for readiness with a bounded timeout. Never wait forever.
- Verify with a real round-trip, not just a liveness ping.
Use scripts/bootstrap.py. Interpreter, host, port, and entrypoint are all
configurable; nothing is hardcoded.
python scripts/bootstrap.py --cwd ./web --entry app:app --port 8000
python scripts/bootstrap.py --check-only # assume already running
Layer 2 — Guard
A watchdog that dies when its subject dies is not a watchdog. Catch per-cycle
exceptions, log them where the user will actually read them, and continue.
Health verdicts are tri-state, never boolean:
| State | Meaning |
|---|---|
OK |
Verified by an actual round-trip |
FAIL-<cause> |
Verified broken, cause named |
UNCONFIRMED |
Present but not exercised this cycle |
A truthy flag in a health payload proves a module loaded, not that it works.
That maps to UNCONFIRMED. Reporting it as OK is the single most common way
a daemon starts lying.
Expensive verification runs every N cycles, not every cycle. Once a deep probe
confirms a structural failure (an unreachable model host, a missing
credential), cache that verdict for the process lifetime — re-probing a known
wall is the retry anti-pattern wearing a costume. Do not cache OK the same
way; success is perishable, structural failure is not.
python scripts/field_guard.py --url http://127.0.0.1:8000/health \
--interval 300 --deep-every 6 --log ./logs/guard.log
python scripts/field_guard.py --once # single cycle, for smoke tests
Layer 3 — Continuity memory
A daemon that recomputes its full state each cycle has no memory; every cycle is
its first. Persist a snapshot, then record only the diff:
cycle 51: conn=OK out=OK in=UNCONFIRMED -> "no change"
cycle 52: conn=OK out=OK in=FAIL-network_wall -> "in:UNCONFIRMED->FAIL-network_wall"
Fifty lines of "no change" plus one transition is a legible history. Fifty full
state dumps are not. Write the delta, not the state.
The memory sink is pluggable — a JSONL file by default, or any callable. If the
sink fails, log it and keep the loop alive; a broken memory must not kill the
body. See references/memory_layer.md for sink contracts and the failure modes
worth knowing before writing one.
Credential and Path Isolation
Mandatory before distributing anything built with this skill.
Never commit or package:
- API keys, tokens, endpoint credentials.
- Absolute interpreter paths tied to one machine (
D:\...\python.exe). - Personal identity files, profile JSON, conversation history.
Resolution order for every setting: CLI flag → environment variable →
config file → documented default. Config files holding secrets carry a
.example twin with placeholder values and are gitignored.
scripts/bootstrap.py --print-config renders the resolved configuration with
secret-shaped values masked, making it safe to paste into a bug report.
Full checklist and a pre-flight scanner: references/credential_isolation.md.
Operating Rules
- Auto-start belongs to the user. Never install a startup entry without an
explicit request, and always ship the matching uninstall path. - Report unavailability plainly. An environment wall is recorded verbatim as
FAIL-<cause>— never retried in hope, never relabeled an "optimization". - Do not emit an aggregate "all systems normal" while any component is
UNCONFIRMED. Name the unconfirmed set every time. - On Windows, keep log output ASCII. The default console encoding (GBK) raises
UnicodeEncodeErroron emoji and can take down an entire cycle.
Relationship to Other Skills
Scoring and rollback criteria for the verdicts produced here are defined by the
presence-self-check skill (the κ model). This skill provides the running body;
that skill provides the honesty discipline. Either works alone.