This is the technical specification for the daemon file format. If you're new to daemons, start with the overview.
Where an agent executes a task with a start, an end, and a definition of done, a daemon fills a role with ongoing responsibilities and no finish line. The daemon file is a role description: purpose defines the mission, routines are standing responsibilities, deny is the authority boundary. Define a role once, not a task every time.
Daemon file
A daemon is a directory containing a DAEMON.md file
.agents/daemons/<daemon-id>/ DAEMON.md # Required scripts/ # Optional: executable helpers the daemon can call references/ # Optional: policy docs, templates, style guides the daemon can read
The DAEMON.md file contains YAML frontmatter followed by markdown content. The frontmatter is declarative. It defines what the daemon is and what it's allowed to do. The content below it is operational guidance the model interprets at runtime.
Frontmatter
| Field | Required | Type | Description |
|---|---|---|---|
| id | Yes | string | Human-readable identifier. Used in logs, notifications, PR comments, and other surfaces. |
| purpose | Yes | string | One sentence describing the daemon's intended outcome. |
| watch | No | list of strings | Event conditions that wake the daemon. Natural language. |
| routines | Yes | list of strings | Operations the daemon performs when activated. Natural language. |
| deny | No | list of strings | Rules the daemon cannot override. Natural language. |
| schedule | No | string | Cron expression for timer-based activation. Standard five-field syntax. |
Validation
- id, purpose, and routines are required.
- At least one of watch or schedule must be present. Both can be present.
- If schedule is present, it must be a valid five-field cron expression.
id
Required · string
A human-readable identifier. IDs are arbitrary strings. The id must match the daemon directory under .agents/daemons/.
purpose
Required · string
One sentence describing the outcome the daemon exists to produce. Write as intent, not mechanics.
watch
Optional · list of strings
Event conditions that wake the daemon. Each entry is a natural-language string describing a discrete, observable event.
watch: - when a pull request is opened - when a maintainer comments "/daemon help" in a PR - when files matching docs/**/*.md are changed
Watch conditions are always event-based. For time-based activation, use schedule. Avoid vague conditions that describe states rather than events:
# Bad: not observable, not an event, too broad watch: - when code quality is low - when the repo needs attention - when code changes
routines
Required · list of strings
Operations the daemon performs when it wakes. Each entry should be a concrete, finite operation that produces an observable result.
routines: - propose clearer PR titles and summaries - identify missing test evidence in PR descriptions - suggest focused follow-up tasks when context is incomplete
A good test: if you can't tell whether the daemon did or didn't perform the routine, it's too vague.
deny
Optional · list of strings
Rules the daemon cannot override. If a routine and a deny rule conflict, the deny rule wins.
Write deny rules for the highest-risk operations adjacent to the daemon's role. The most important deny rules aren't about things the daemon would never think to do. They're about things it might reasonably attempt as a natural extension of its routines.
deny: - merge pull requests - approve pull requests on behalf of humans - push commits directly to protected branches - delete files or directories
schedule
Optional · string
A cron expression for timer-based activation. Standard five-field syntax: minute, hour, day-of-month, month, day-of-week.
0 9 * * * # daily at 09:00 0 */6 * * * # every 6 hours 30 14 * * 1-5 # weekdays at 14:30
Content
The markdown body below the frontmatter is where you define how the daemon operates. There are no format restrictions. Write whatever helps the model execute the daemon's role effectively. Commonly useful sections:
| Section | Purpose |
|---|---|
| Policy | Rules, standards, and decision logic. Concrete rules the daemon can follow unambiguously. |
| Output format | Structure of what the daemon produces. Without this, the daemon will invent a different format every time. |
| Escalation | When to hand off to a human. |
| Limits | Batch size, rate limiting, pacing. |
| Scope | Repos, directories, file types. |
| Priority | What to work on first. |
| Thresholds | Numeric boundaries for detection. |
| Conventions | Team norms to follow. |
| Coordination | How the daemon relates to other daemons and humans. |
| Ignore patterns | What to skip. |
| Examples | Concrete samples of good and bad output. |
Activation model
A daemon can activate in three patterns:
- Watch-only — wakes when a watch condition matches an event.
- Schedule-only — wakes when the cron schedule fires.
- Hybrid — both watch and schedule are present. Either can wake the daemon independently.
Each wake-execute cycle is one activation. The daemon wakes, receives context about why it woke, executes its routines within its deny rules and content guidance, and completes.
Examples
Complete daemon files
PR Helper (watch-only)
A daemon that reduces time to merge by fixing merge-blocking issues before they require a human round-trip.
---
id: pr-helper← identity
purpose: Reduces time to merge by fixing merge-blocking issues before they require a human round-trip.← intent
watch:← events
- when a pull request is opened
- when a pull request is synchronized
- when a check run or status check changes on a pull request
- when a commit is pushed to master
routines:← operations
- safely resolve merge conflicts
- fix and retry failing checks
- keep pull request titles and bodies updated and accurate
deny:← constraints
- merge pull requests
- approve pull requests
- push commits directly to protected branches
- edit production configuration or secrets-related files
---
## Policy
- Act by default. When a blocker is mechanical, localized, and reversible, fix it.
- Keep changes narrow. Prefer the smallest change that returns the PR to a merge-ready state.
- Do not broaden scope with opportunistic refactors or style-only edits unrelated to the blocker.
- Apply code changes only on the PR branch. Never write directly to the base branch.
## Scope
- Only operate on open, non-draft pull requests.
- Focus only on merge-readiness blockers: merge conflicts, failing checks, outdated metadata.
## Escalation
- Escalate when the PR touches security-sensitive areas.
- Escalate when the correct resolution requires product decisions or broad refactors.
- Escalate after repeated failed fixes.
## Limits
- Post at most 1 comment per PR per activation.
- Retry flaky failures at most 2 times per SHA.
## Output format
Post a short plain-English comment when the daemon pushed a change, found a blocker it
could not fix, or needs human input. Include commit SHAs and links to failed runs.
Librarian (hybrid)
A daemon that keeps repository documentation current by detecting stale docs on every merge and sweeping for gaps on a daily schedule.
---
id: librarian← identity
purpose: Keeps repository documentation current and complete.← intent
watch:← events
- when a pull request is merged into the default branch
schedule: "0 9 * * *"← timer
routines:← operations
- detect stale docs based on recent code changes and update them
- create new docs for undocumented packages, apps, and services
- fix broken links and heading hierarchy in docs
deny:← constraints
- changes to files that aren't documentation
- changes to compliance or legal documentation
- changes to changelogs or release notes
---
## Policy
- Act by default. When a doc is clearly stale and the correct content can be inferred
from code, fix it instead of flagging it.
- Every claim in a doc update must be supported by the current codebase.
- Preserve author voice when updating existing docs.
## Limits
- Push at most 3 commits per activation.
- Maintain at most 1 open PR at a time.
- Stop if the open PR reaches 10 commits or 5,000 lines changed.
## Output format
All communication goes in the librarian's own PR body as a rolling list of changes,
each starting with the commit SHA followed by what changed and why.
Bug Triage (watch-only)
A daemon that ensures every bug issue is complete, prioritized, assigned, and root-cause-analyzed.
---
id: bug-triage← identity
purpose: Ensures every bug issue is complete, prioritized, assigned, and root-cause-analyzed so engineers can start fixing immediately.← intent
watch:← events
- when a Linear issue is created with the bug label
- when the bug label is added to a Linear issue
routines:← operations
- set priority based on Sentry impact data and issue context
- assign the issue to the owner determined by CODEOWNERS
- append missing context to the issue body
- post a root cause analysis comment with supporting evidence
deny:← constraints
- create or delete Linear issues
- add or remove labels on Linear issues
- change the status of Linear issues
- override priority or assignee that was set by a human
---
## Policy
- Only fill in what is missing. If a human already set priority, assignee, or wrote a
complete body, leave it alone.
- Every claim in the RCA must be supported by evidence: a stack trace, error frequency
data, or a specific code reference. Do not speculate.
## Escalation
- If the bug appears to involve a security vulnerability, flag it and stop.
- If the root cause spans multiple services and cannot be isolated, state this clearly.
## Output format
Post exactly one RCA comment per issue: root cause, evidence, and confidence/gaps.
Do not suggest fixes. The RCA identifies the problem; humans decide the solution.
Provider Guide
Building daemon support into a runtime
This section is for teams building daemon support into a cloud-based AI agent runtime. It explains how to interpret the daemon file format and what infrastructure is needed to run daemons.
Daemons extend the Agent Skills model into persistent, background processes. Where skills activate on demand during a conversation, daemons activate on events and schedules with no human in the loop. The daemon author drops a markdown file in their repo. The provider's job is to make the rest seamless: discovery, activation, and execution, without the team monitoring uptime or restarting processes.
File discovery
Scan repositories for DAEMON.md files at .agents/daemons/*/DAEMON.md. Each subdirectory under .agents/daemons/ represents one daemon. If a scripts/ or references/ directory exists alongside the daemon file, it is part of that daemon's support tree and should be accessible to the model during execution.
A repository can contain multiple daemons. Each operates independently.
Parsing and validation
Extract the YAML frontmatter from the markdown body. Validate that required fields (id, purpose, routines) are present and that at least one of watch or schedule is defined. If schedule is present, validate that it is a parseable five-field cron expression.
How you handle invalid daemon files is up to you. Options include rejecting the file at registration time, warning the team, or skipping the daemon silently. Surfacing errors to the team is recommended so authors can fix problems.
Watch condition mapping
Watch conditions are natural-language strings that describe events. Your runtime is responsible for interpreting them and mapping them to concrete event sources: webhooks, event subscriptions, polling, or whatever mechanism your platform supports.
The spec does not define a canonical set of event types. Authors write conditions like "when a pull request is opened" or "when files matching docs/**/*.md are changed" and the provider interprets them. This keeps daemon files portable across providers while allowing each provider to support events through whatever infrastructure they have.
Common phrasings include:
when a pull request is opened when a pull request is updated / synchronized when a pull request is merged when a PR comment is created when an issue is created / updated / labeled when files matching <pattern> are changed when a Sentry alert fires when a commit is pushed to <branch>
These examples are illustrative, not normative. When a watch condition doesn't map to any event source your platform supports, the handling is at your discretion.
Schedule evaluation
If a schedule field is present, evaluate it as a standard five-field cron expression (minute, hour, day-of-month, month, day-of-week). Wake the daemon when the cron expression fires.
Timezone handling, missed-schedule behavior, and jitter are implementation details left to the provider.
Activation
An activation is a single wake-execute cycle. The daemon wakes, receives context, executes, and completes.
When both watch and schedule are present, either can wake the daemon independently. A schedule firing does not suppress pending watch activations, and vice versa.
At activation time, the model should receive enough context to execute effectively. A good activation context includes:
- The full daemon file (frontmatter and content).
- The event payload or schedule tick that triggered the activation.
- Access to the daemon's support tree (scripts/, references/).
- State from prior activations if your runtime supports persistent memory.
The richer the context, the better the model can follow instructions like limits ("no more than 3 PRs per day") and coordination ("don't comment on PRs with an active human review").
Content interpretation
Everything in the daemon file body, plus routines and deny from the frontmatter, is natural language interpreted by the model at runtime. The quality of interpretation is the provider's differentiator.
The contract
Pass the full daemon file to the model faithfully. Do not truncate, rewrite, or selectively omit sections. The model receives the complete frontmatter and content as authored by the customer.
If the content exceeds your model's context window, the handling is at your discretion, but the default should be to include the full file. Daemon files that are too large for the context window are an authoring problem, not a runtime problem.
What the model interprets
All of the following are model-interpreted, not mechanically enforced:
| Component | How it's handled |
|---|---|
| Routines | The model decides how to execute each routine based on the activation context. |
| Deny rules | Enforced through model compliance, not runtime interception. |
| Content sections | Policy, limits, escalation, output format, scope, and everything else the author writes. |
The distinction matters: your runtime mechanically handles file discovery, event mapping, cron evaluation, and activation. Everything else is the model's job.
Support tree access
Make the support tree available to the model during execution. Scripts should be executable. Reference files should be readable as context. The model decides when and how to use them based on the content instructions.
Platform requirements
Execution environment
Daemons need a compute environment with network access to the team's integrated systems: GitHub, Linear, Slack, Sentry, and other systems of record. Credential management and permission scoping are important. Isolating daemon execution so one daemon cannot interfere with another is recommended.
Supported Platforms
These are the required capabilities for a platform to support daemons.
| Capability | Charlie Labs |
|---|---|
| Documented support | ✓ |
| Daemons run autonomously in the cloud by default | ✓ |
| Supports watch conditions | ✓ |
| Provides execution guarantees for schedule conditions | ✓ |
| Maintains a compute environment with network access to the team's integrated systems: GitHub, Linear, Slack, Sentry, and other systems of record. | ✓ |
| Maintains daemon state persistence across activations | ✓ |
| Manages daemon lifecycle (activates on CRUD actions related to daemons) | ✓ |
Output delivery
Daemons produce work in native surfaces: PR comments and commits in GitHub, issue updates in Linear, messages in Slack. Your runtime needs integrations with the systems the daemon writes to.
Daemon output should be attributable. Teams need to distinguish daemon-produced work from human work. How you implement attribution (bot accounts, labels, metadata) is up to you.
Idempotency matters. If an activation is retried, the daemon should not duplicate its output.
State persistence
Daemons benefit from state that persists across activations. Activation history, accumulated context, and counter state all help the model follow instructions more accurately over time.
A daemon with no persistent state can still function, but it will be unable to follow instructions that reference prior activations and will not improve through accumulated context.
Lifecycle management
When a daemon file is added, modified, or removed from a repository, your runtime needs to respond. Common approaches include watching for commits that touch .agents/daemons/ and updating registrations accordingly.
Portability
Daemon files are intended to be portable across providers. A daemon file authored for one runtime should work on another without modification.
Portability depends on providers converging on similar interpretations of natural-language watch conditions. The spec intentionally leaves interpretation to the provider, but common phrasings like "when a pull request is opened" should be interpreted consistently across providers.
If your runtime supports event types that other providers may not, document them so daemon authors can make informed decisions about portability.