# AGENTS.md instructions Source: https://docs.charlielabs.ai/AGENTS.md-instructions Write durable repo instructions that Charlie loads by default. Charlie reads instructions from `AGENTS.md`. For most teams, the best setup is a single **root-level** `AGENTS.md` that defines your default operating rules for planning, coding, reviews, and communication. ## Instructions template Use this compact template and keep rules stable over time: ````md theme={null} #
## Scope - Paths, packages, or contexts this section applies to. ## Context - Team- or domain-specific background Charlie should know. ## Rules - [R1] - [R2] - [R3] ## Examples ### Good - [R1] ```ts // minimal example ``` ### Bad - [R2] ```ts // anti-pattern ``` ```` ## Instructions vs. skills * **Instructions (`AGENTS.md`)** define persistent policy and defaults. * **Skills (`.agents/skills/*/SKILL.md`)** define reusable task-shaped playbooks you invoke with `$`. Use instructions for durable rules. Use skills for repeatable workflows. ## Common mistakes to avoid * Putting task-by-task checklists in `AGENTS.md` (that belongs in skills). * Duplicating the same rule across multiple files. * Writing vague guidance without a default action. ## Related docs * [Skills](/skills) * [How it works](/how-it-works) # Connect AI tools to the docs Source: https://docs.charlielabs.ai/connect-to-docs-mcp Use the unauthenticated Charlie Labs documentation MCP server with Codex, Claude Code, Cursor, or other MCP-compatible tools. Use the unauthenticated Charlie Labs documentation MCP server with Codex, Claude Code, Cursor, or any other MCP-compatible tool. ```text theme={null} https://docs.charlielabs.ai/mcp ``` # Daemons Source: https://docs.charlielabs.ai/daemons Understand what Charlie daemons are, how they wake, and how DAEMON.md controls behavior. **Human readers:** the fastest way to get started with daemons is to work directly with Charlie. **Charlie can help you** suggest where to start and add your first daemon. Use the prompts below to ask Charlie in Slack, GitHub, or Linear. ```markdown Learn about daemons theme={null} Explain what daemons are and how they work. ``` ```markdown Daemon use cases theme={null} What are some use cases for daemons? Which would you recommend starting with? ``` ```markdown Add a daemon theme={null} Open a PR to add a simple daemon to my repo. Explain what the daemon does and why you think it's a good place to start. ``` *** Daemons are repo-defined operating roles for Charlie. They give recurring operational debt an explicit owner instead of leaving it to ad hoc cleanup. A daemon lives at: `.agents/daemons//DAEMON.md` The daemon file tells Charlie: * its `purpose` * what `watch` conditions or `schedule` can wake it * what `routines` it should perform when it wakes * what `deny` rules tell it not to do * any additional operating guidance in the markdown body ## Start here If you are a human reader, the intended workflow is to work with Charlie. Ask Charlie to: * explain what daemons are * explain how to use daemons * recommend which daemons to add * create a new daemon file * edit an existing daemon file * help you test and iterate safely For the docs path, use the pages listed in **Where to go next** below. ## Use the daemon CLI Use the daemon CLI when you want the quickest path to browse examples, inspect an example before adapting it, scaffold a draft into a repository, or validate local daemon files. ```bash theme={null} bunx @charlie-labs/daemons --help bunx @charlie-labs/daemons list bunx @charlie-labs/daemons show pr-metadata bunx @charlie-labs/daemons add pr-metadata --dry-run bunx @charlie-labs/daemons validate --all ``` `list` and `show` are read-only browsing commands. `add --dry-run` previews the files that would be written, and `validate --all` checks local `.agents/daemons/**/DAEMON.md` files against the runtime file contract. Scaffolding writes draft files only. A scaffolded daemon does not become eligible for live activations until the change is merged to the repository default branch and Charlie ingests that merged version. ## What daemons are A daemon is a persistent role definition for recurring, bounded maintenance or operational work. Teams use daemons when there is ongoing maintenance or operational judgment that should be owned explicitly instead of handled ad hoc. Across activations, daemons can leverage prior activation logs to maintain state. Dedicated daemon memory is coming soon. ## What daemons are not A Charlie daemon is different from: * **Unix daemons:** a Charlie daemon is a repo-authored operating role, not a long-running OS process. * **Cron jobs:** a Charlie daemon is not just fixed predetermined steps on a timer. * **GitHub Actions:** a Charlie daemon is not a CI/workflow file tied to one pipeline. * **Fixed-rule bots:** watch matching is semantic rather than deterministic. * **Open-ended prompts:** a Charlie daemon is bounded by repo-local policy, `routines`, `deny`, and wake conditions. ## How daemons work today Today, each activation follows a wake/execute model: 1. the daemon wakes because `watch` matched or `schedule` fired 2. context from past activations of the daemon is loaded 3. Charlie executes the daemon’s routines within daemon constraints 4. the activation completes Supported wake sources are: * routed GitHub events * routed Linear events, including issue creation, issue comments, Charlie issue mentions, and issues assigned to Charlie * routed Slack events, including mentions and thread replies, channel messages, and DMs * scheduled wakes from the daemon’s `schedule` field For GitHub, event-driven activation uses the repo-scoped GitHub App installation. For Linear, it requires a connected Linear workspace and the issue's Linear team mapped to a repo with daemon inventory. For Slack, it requires a connected Slack workspace mapped to a repo with daemon inventory. Write `watch` around observable GitHub, Linear, or Slack events; use execution-time policy for derived state that requires API queries or analysis. Schedule-driven daemon activation uses the daemon’s `schedule` field when present as a standard five-field cron expression. ## `DAEMON.md` is the control surface `DAEMON.md` is the authored control surface for a daemon. The file has two parts: * YAML frontmatter for the formal contract * a markdown body for operating guidance The frontmatter contains the authored fields: * `id` * `purpose` * `watch` * `routines` * `deny` * `schedule` Minimum validity constraints: * `id`, `purpose`, and `routines` are required. * At least one of `watch` or `schedule` is required. The body is freeform markdown. Use it for the operating rules that make the daemon consistent, such as: * decision policy * limits * communication behavior * verification * freshness Those headings are conventions in the body, not extra frontmatter schema. Charlie treats the attached daemon file as the daemon’s primary role policy for each activation. ## Wake model A daemon can be: * watch-driven * schedule-driven * both `watch` describes event conditions that can wake the daemon. `watch` explains why a daemon wakes; it does not by itself authorize mutation. `schedule` describes time-based wakes using a standard five-field cron expression. If both are present, the daemon can wake from either path. At runtime, Charlie may refer to the daemon as: * Watch-only * Schedule-only * Hybrid Those are runtime-derived labels. Do not author them as frontmatter fields. ## Key terms * **daemon**: a repo-defined Charlie role for recurring maintenance or operational work * **daemon ID**: the canonical identifier, represented by the directory name and required `id` field * **daemon file**: the `DAEMON.md` file that defines the role * **frontmatter**: the YAML contract at the top of the file * **body**: the markdown guidance below the frontmatter * **wake / activation**: one execution caused by an event or schedule * **wake context**: the event or schedule reason the daemon woke for this activation * **watch**: event conditions that can wake the daemon * **routines**: the concrete operations the daemon performs when it wakes * **deny**: things the daemon must not do * **schedule**: timer-based wake configuration * **native surfaces**: the systems where teams already review and act on daemon output, such as GitHub, Linear, and Slack ## Where to go next * Read [Choosing daemons](/daemons/choosing-daemons) when deciding what daemon roles a team or repo should have. * Read [Writing and editing `DAEMON.md`](/daemons/writing-and-editing-daemon-md) when creating a new daemon or improving an existing one. * Use the [Review Checklist](/daemons/review-checklist) when reviewing daemon-file changes before merge. * Read [Testing and iterating on daemons](/daemons/testing-and-iterating-on-daemons) before broad rollout. * Read [`DAEMON.md` reference](/daemons/daemon-md-reference) when you need the exact file contract. * Use the daemon CLI for the quick path to browse examples and validate local daemon files. Start with `bunx @charlie-labs/daemons list`, `bunx @charlie-labs/daemons show `, and `bunx @charlie-labs/daemons validate --all`. * Use the examples repo for concrete reference patterns, but start with its [README.md](https://github.com/charlie-labs/daemons/blob/master/README.md) and treat the examples as patterns to adapt, not as files to copy blindly. # Choosing daemons Source: https://docs.charlielabs.ai/daemons/choosing-daemons Choose the right daemon roles using practical selection heuristics, scoping rules, and rollout fit. **Human readers:** the fastest way to get started with daemons is to work directly with Charlie. **Charlie can help you** suggest where to start and add your first daemon. Use the prompts below to ask Charlie in Slack, GitHub, or Linear. ```markdown Learn about daemons theme={null} Explain what daemons are and how they work. ``` ```markdown Daemon use cases theme={null} What are some use cases for daemons? Which would you recommend starting with? ``` ```markdown Add a daemon theme={null} Open a PR to add a simple daemon to my repo. Explain what the daemon does and why you think it's a good place to start. ``` *** Before choosing daemon roles, read [Daemons](/daemons). In this docs set, a daemon is a repo-defined operating role for Charlie, authored in `.agents/daemons//DAEMON.md`. That is not the same thing as a Unix daemon, a cron job, a GitHub Action, or a generic background agent. Use this page when Charlie or a human is deciding what daemon roles a team or repo should have. This page is about choosing the right daemon, not about the exact file format. For the file contract, read [DAEMON.md reference](/daemons/daemon-md-reference). For how to write or edit a file, read [Writing and editing DAEMON.md](/daemons/writing-and-editing-daemon-md). ## How to identify daemon opportunities Do not start by asking, “What clever daemon could we build here?” Start by asking: * What recurring maintenance or operational work is happening here? * What recurring maintenance or operational work is not happening, but should be? * What low-grade operational debt is accumulating because nobody consistently owns it? Daemons are a good fit when the work is recurring, bounded, and worth doing continuously. ## Selection heuristics Charlie should apply When choosing candidate roles, Charlie should usually: * prioritize the most repetitive and under-owned maintenance work * prefer narrow roles with obvious activation conditions * prefer daemon shapes that produce legible output on native surfaces “Obvious activation conditions” means concrete observable evidence: a PR opened, a review submitted, a file path changed, a stale-item threshold crossed, or a recurring survey on a schedule. It does not mean vague intent like “when the repo needs help.” Good daemon candidates need wake conditions based on events or signals Charlie can actually observe. If the desired target state requires a follow-up query or derived analysis, wake on the closest observable event or schedule, then inspect the derived state during execution. If a proposed role is broad, hard to wake reliably, or hard to review where the team already works, it is probably not the right first daemon. ## What makes a good daemon candidate A good daemon candidate is: * **recurring**: the work comes up repeatedly * **bounded**: the role can be described clearly enough to know what is in and out of scope * **observable**: there are events, schedules, or state changes Charlie can use to wake the daemon * **reviewable**: the daemon’s outputs can be seen and judged on native surfaces * **valuable even when done incrementally**: each activation can do a small amount of useful work A strong first daemon is usually narrow. It does one ongoing job well and stops/no-ops, comments with a blocking reason, or asks for specific human input when work falls outside that job. Scheduled daemon roles are strongest when each activation can create value on its own. Frame them as recurring surveys, checks, or reconciliation passes, not as open-ended background ambition. Activations should stay bounded, but runs can still use context from prior run logs. ## Daemon or one-off Charlie task? Use a daemon when the same bounded role should wake repeatedly and add value over time. Use a normal Charlie task when: * the work has a clear finish line * the request is specific to one temporary push, investigation, or project moment * the work depends heavily on transient human context * the work is useful now but not worth maintaining as an ongoing role A useful test is: if this were handled well once, would we still want the same role to keep waking later? If yes, it may be a daemon. If no, it is probably a one-off Charlie task. ## What not to daemonize Do not daemonize work that is: * one-off * vague * broad enough to read like “improve everything” * dependent on hidden human context that the daemon cannot reliably infer * dependent on continuously running process semantics instead of bounded activations with clear wake signals * too risky to act on without explicit stop/comment/action boundaries * only useful as an ad hoc investigation or project-specific push If the work is better framed as one normal task for Charlie, use a normal task instead of a daemon. ## Use-case families The families below are representative, not exhaustive. They are broader than the current public example set. Use the nearest example as a pattern anchor, not as a boundary on what is allowed. ### PR maintenance Use a daemon when PRs routinely: * go stale * lose mergeability * have weak titles or descriptions * miss expected evidence * accumulate fixable hygiene issues that nobody owns * carry review feedback that needs ongoing triage Example repo-local daemon files: `.agents/daemons/pr-metadata/DAEMON.md`, `.agents/daemons/pr-review-triage/DAEMON.md`, `.agents/daemons/pr-check-repair/DAEMON.md`, `.agents/daemons/pr-feedback-repair/DAEMON.md`, and `.agents/daemons/pr-merge-conflict-repair/DAEMON.md`. ### Issue or project-graph hygiene Use a daemon when: * issue status drifts from reality * labels are missing or inconsistent * PRs and issues are not linked correctly * completed work is not being closed or updated * project metadata is expected to stay structured, but nobody maintains it * new issues need labels, owners, or reproduction details before work can start * linked PR state needs regular reconciliation with issue status * ownership or follow-up expectations become unclear as issue threads evolve ### Documentation freshness Use a daemon when: * docs drift behind implementation * README and setup instructions become unreliable * operational docs or runbooks rot quietly * documentation maintenance is clearly needed but never prioritized ### Dependency or codebase maintenance Use a daemon when: * dependencies age without review * low-risk mechanical fixes pile up * dead code or scaffolding accumulates * low-grade maintenance work exists but nobody owns it continuously ### Bug or error triage Use a daemon when: * bug reports or alerts pile up without triage * repeated issues disappear into noise * someone needs to maintain a structured view of what deserves human attention * duplicates, conflicts, or stale feedback regularly waste time * bug reports arrive in support channels and need filing, deduping, or linking to the right issue * incident or support threads collect decisions and blockers that should be summarized * recurring support summaries would make the highest-risk reports easier to review ### Scheduled surveys, checks, or reports Use a daemon when: * the most useful work is not tied to one event * you need recurring review of a surface * the job is “notice what needs attention on a schedule” rather than “react immediately to one signal” * the best role is periodic reconciliation, ranking, or reporting ## When to choose `watch`, `schedule`, or both Choose **watch** when the daemon should react to a discrete event. Choose **schedule** when the daemon should wake on time, survey a scope, and decide what matters now. Choose **both** when the daemon needs: * immediate reaction to some events * plus periodic review for issues that may not emit a useful event Use `watch` for event-driven work. Use `schedule` for time-driven work. Use both only when both wake postures materially help the same daemon role. Signal-driven daemon inference can evaluate routed GitHub, Linear, and Slack events once Charlie can infer the relevant repo and daemon inventory. For Linear-heavy roles, make the issue team mapping and semantic `watch` evidence explicit. For Slack-heavy roles, make the workspace mapping and semantic `watch` evidence explicit. Use `schedule` or a hybrid when the role also needs periodic review beyond individual events. For survey-style event daemons, wake on the narrow event that makes the survey newly relevant, then inspect candidates during execution. Do not broaden the wake condition just to encode every possible candidate state. ## How to scope a daemon well This section is about choosing clean daemon role boundaries. It is not a recommendation to add a generic body section named for scope; hard target boundaries should live in frontmatter when possible, with body policy reserved for nuanced target selection. Keep one daemon when: * the work serves one narrow purpose * the wake logic is closely related * the risk profile is similar * the outputs belong on the same native surfaces Split into multiple daemons when: * the role is trying to do too many unrelated things * some routines need very different wake logic * some routines need very different limits or stop/comment/action boundaries * some routines would be easier to reason about as smaller roles * different kinds of output belong on different native surfaces When in doubt, split broad ideas into smaller daemon candidates and start with the narrowest useful version. If a daemon would need many runtime branches for package manager, issue tracker, provider, or platform-specific behavior, prefer a narrower daemon or a single configured variant. A specific, adapted daemon is usually easier to trust than a generic daemon that guesses its operating mode. A good first daemon is usually narrower than the daemon you might eventually want. ## How to choose the first daemon Do not start with the most ambitious daemon. Start with the recurring maintenance work that is: * clearly under-owned * high-friction for the team when neglected * easy to bound * easy to review on native surfaces * likely to create value without high-risk side effects * likely to produce signal rather than noise In practice, that usually means starting with one obvious maintenance role rather than a “repo caretaker” that tries to own everything. ## Value versus noise A daemon is more likely to add value when: * its wake conditions are concrete * its outputs appear where the team already reviews that kind of work * its actions are easy to verify * its scope is narrow enough to explain in one sentence * each activation can do a small amount of useful work without broad wandering Warning signs that a daemon will mostly create noise: * the wake condition is vague * the role touches too many unrelated surfaces at once * the daemon mostly restates known information without changing decisions or state * the outputs land somewhere the team does not already monitor * the role depends on too much hidden human context * the daemon would create follow-up work faster than the team can absorb it * the role is so broad that almost every activation would need judgment outside clear boundaries ## What Charlie needs in order to recommend daemons well When asking Charlie to recommend daemons, provide: * the recurring maintenance problems you want daemons to own * the repo or team surfaces where that work appears today, such as GitHub, Linear, or Slack * the areas of the repo or workflow that matter most * any boundaries the daemon should respect * any especially noisy areas that should be avoided If the team already has strong norms, include them. Daemon selection is much better when Charlie knows what “healthy” looks like for this team. ## Native-surface fit Choose outputs that match the system of record the daemon is maintaining. As a rule: * use GitHub-facing output for PR, repo, and codebase maintenance * use Linear-facing output for issue or project-graph hygiene * use Slack-facing output for summaries, low-blast-radius rollout, or cases where the right first step is visibility rather than mutation A daemon should produce work where the team already expects to see and review that kind of work. If a daemon’s output would feel out of place on the target surface, re-scope the daemon or choose a different surface. ## How to use examples when choosing daemons Start with the daemon CLI so you can browse the current catalog and inspect the nearest pattern before opening source files. ```bash theme={null} bunx @charlie-labs/daemons list bunx @charlie-labs/daemons show pr-metadata ``` Use `list` to see available example IDs. Use `show ` to inspect whether an example is ready to adapt: readiness, required and optional integrations, support files, and required adaptation notes are surfaced before you copy anything. Then use the examples repo README as a supplemental browsing path: [examples repo README](https://github.com/charlie-labs/daemons/blob/master/README.md). Use examples to: * find the nearest existing pattern * calibrate scope * see how similar daemon roles are bounded * compare different wake shapes and output postures * understand how narrow, explicit roles are actually written Do not treat the examples as the full space of valid daemons. Use them as pattern anchors, not as a menu of all allowed roles. If no example exactly matches the repo’s problem, choose the nearest pattern and adapt the role to the repo’s actual maintenance gap. Where the current public examples are PR-heavy, take the role shape, scoping discipline, and output posture from them rather than copying the literal surface. ## A simple selection rubric A daemon idea is promising when the answer is “yes” to most of these: * Is this work recurring? * Is the role narrow enough to explain in one sentence? * Can the daemon wake from clear events, schedules, or both? * Can the team review the daemon’s outputs on native surfaces? * Would small, repeated activations be useful here? * Is this likely to create signal rather than noise? * Can the daemon be rolled out narrowly first? A daemon idea is weak when the answer is “no” to most of these. ### Recommendation clarity check for daemon recommendations When Charlie recommends daemons, the recommendation should make these points explicit: * the daemon’s purpose * why this role is worth creating now * whether it should be watch-driven, schedule-driven, or hybrid * what makes the role narrow enough to start safely * where the daemon’s output should appear * which example daemon is the closest analog, if any If Charlie cannot explain those clearly, the daemon idea is probably not ready. # DAEMON.md reference Source: https://docs.charlielabs.ai/daemons/daemon-md-reference Exact DAEMON.md authored contract, validation rules, and support-tree semantics. **Human readers:** the fastest way to get started with daemons is to work directly with Charlie. **Charlie can help you** suggest where to start and add your first daemon. Use the prompts below to ask Charlie in Slack, GitHub, or Linear. ```markdown Learn about daemons theme={null} Explain what daemons are and how they work. ``` ```markdown Daemon use cases theme={null} What are some use cases for daemons? Which would you recommend starting with? ``` ```markdown Add a daemon theme={null} Open a PR to add a simple daemon to my repo. Explain what the daemon does and why you think it's a good place to start. ``` *** This page defines the authored `DAEMON.md` contract. A daemon is not a Unix daemon, cron job, GitHub Action, or generic background agent. In this system, `DAEMON.md` is the authored control surface for a repo-local Charlie role, while runtime metadata such as activation mode is derived rather than authored directly. Use this page when you need the exact file format, field definitions, validation rules, and support-tree semantics. For guidance on when to use a daemon, read [Choosing daemons](/daemons/choosing-daemons). For guidance on how to write a good daemon, read [Writing and editing `DAEMON.md`](/daemons/writing-and-editing-daemon-md). For concrete examples, start from the examples repo README/index. ## Directory structure A daemon is a directory containing a `DAEMON.md` file. ```text theme={null} .agents/daemons// DAEMON.md scripts/ # optional references/ # optional ``` ## Full file format `DAEMON.md` must contain: 1. `---`-delimited YAML frontmatter that parses to an object/map 2. a markdown body below the closing frontmatter delimiter The authored file shape is: ```md theme={null} --- id: purpose: watch: - routines: - deny: - schedule: "" --- ``` The file is invalid if: * the opening or closing `---` delimiter is missing * the frontmatter is malformed YAML * the frontmatter does not parse to an object/map ## Field-by-field reference ### Authored frontmatter fields | Field | Required | Type | Meaning | | ---------- | -------- | --------------- | --------------------------------------------------------------------- | | `id` | Yes | string | Canonical daemon identifier. Must exactly match the daemon path slug. | | `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 | Behavioral prohibitions for the daemon. Natural language. | | `schedule` | No | string | Timer-based wake configuration. Standard five-field cron. | ### `id` `id` is the canonical daemon identifier. Rules: * required * non-empty * must exactly match the daemon directory name ### `purpose` `purpose` is one sentence describing the daemon’s intended outcome. Write intent, not mechanics. ### `watch` `watch` is a list of natural-language event conditions that can wake the daemon. Rules: * optional * if present, must be a YAML list of non-empty strings * should describe discrete, observable events Use provider-visible event language rather than raw webhook names. Keep provider-specific examples and routing requirements in [Writing and editing `DAEMON.md`](/daemons/writing-and-editing-daemon-md) and the relevant integration docs. Use `schedule` for cron-based wakes. Do not put cron timing in `watch`. ### `routines` `routines` is a list of natural-language operations the daemon performs when it wakes. Rules: * required * must be a YAML list of non-empty strings * must include at least one entry * each entry should describe a concrete, finite operation ### `deny` `deny` is a list of things the daemon is told not to do. Rules: * optional * if present, must be a YAML list of non-empty strings ### `schedule` `schedule` is timer-based wake configuration. Rules: * optional * when present with meaningful text, must be a string * blank or whitespace-only values are treated as `null` * must be valid standard five-field cron * evaluated in UTC Supported cron semantics today: * standard five-field cron only * comma-separated lists * ranges * stepped ranges * `7` in day-of-week is treated as `0` (Sunday) Day matching follows normal cron-style rules: * if both day-of-month and day-of-week are wildcard, any day matches * if one is wildcard, the other controls * if both are restricted, the scheduler matches `day-of-month OR day-of-week` ## Validation rules These are the key authored validation rules. * `id` is required. * `id` must exactly match the daemon path slug. * `purpose` is required. * `routines` is required. * `routines` must be a YAML list of strings and must contain at least one non-empty entry. * `watch`, when present, must be a YAML list of non-empty strings. * `deny`, when present, must be a YAML list of non-empty strings. * `schedule`, when present with meaningful text, must be a string. * Blank or whitespace-only `schedule` values are treated as `null`. * At least one activation field must be present: `watch` or `schedule`. * `schedule` must be a valid five-field cron expression. * Activation mode is derived from `watch` and `schedule`; do not author it directly. Runtime-derived activation labels are: * **Watch-only**: `watch` present, `schedule` absent * **Schedule-only**: `schedule` present, `watch` absent * **Hybrid**: both present ## Validate with the CLI Use the daemon CLI to validate runtime daemon files in a repository. ```bash theme={null} bunx @charlie-labs/daemons validate .agents/daemons/pr-metadata/DAEMON.md bunx @charlie-labs/daemons validate --all bunx @charlie-labs/daemons validate --all --json ``` `validate ` checks one file. `validate --all` discovers `.agents/daemons/**/DAEMON.md` files from the current repository. Add `--json` when automation needs structured output. Validation checks the runtime file contract: frontmatter syntax, allowed and required fields, activation fields, cron syntax, a non-empty markdown body, and path-aligned IDs. ## Field name disambiguation Use the authored field names exactly as written in this page. | Use this | Do not use | | ---------- | ------------- | | `id` | `name` | | `purpose` | `description` | | `watch` | `triggers` | | `routines` | `actions` | | `deny` | `disallowed` | `name` is not a valid authored field. Use `id`. Do not invent extra frontmatter fields. Do not add runtime-derived labels such as activation mode to frontmatter. ## Body semantics The markdown body below the frontmatter is freeform. It is meaningful at runtime: the body is carried into execution and interpreted as part of the daemon’s operating brief. Because the body is interpreted at runtime, do not use it for install-time setup, copy-time adaptation, or tutorial notes. Use it for operational guidance such as: * decision policy * communication policy * verification policy * freshness policy * concurrency policy * output format * limits * priority * thresholds * conventions * ignore patterns * examples There are no required body headings. Headings such as `Decision policy`, `Limits`, `Communication policy`, and `Verification and freshness` are conventions, not schema. ## Support tree semantics A daemon directory may include optional subdirectories alongside `DAEMON.md`: ```text theme={null} .agents/daemons// DAEMON.md scripts/ references/ ``` Use: * `scripts/` for executable helpers or deterministic utilities the daemon can call * `references/` for policy docs, templates, style guides, or other context the daemon can read but should not modify If a daemon relies on a helper script, document the required inputs, output shape, pagination or completeness expectations, and what the daemon should do when the script cannot return complete context safely. ## Authoring conventions that help avoid validation issues Use these conventions consistently: 1. **Canonical field order.** When fields are present, use this order: Treat this order as a consistency/readability convention; parsers must not require it. * `id` * `purpose` * `watch` * `routines` * `deny` * `schedule` 2. **Path-aligned IDs.** Keep the directory name and the `id` value exactly aligned. 3. **Stable naming.** Use only the canonical authored field names: `id`, `purpose`, `watch`, `routines`, `deny`, and `schedule`. ## Examples For concrete patterns, use the examples repo index: [Examples repo README](https://github.com/charlie-labs/daemons/blob/master/README.md). # Review Checklist Source: https://docs.charlielabs.ai/daemons/review-checklist Review daemon-file pull requests for contract, safety, repeatability, and proof gaps before merge. ## 1. Frequently broken rules Use these as first-pass review gates before reading the detailed checklist.
  1. Put the durable daemon contract in frontmatter. Identity, purpose, wake conditions, core routines, hard prohibitions, and schedules belong in `id`, `purpose`, `watch`, `routines`, `deny`, and `schedule`, not body prose.
  2. Do not duplicate frontmatter in the body. If a body section restates `purpose`, `watch`, `routines`, or `deny`, delete it or move only the missing hard rule into frontmatter.
  3. Do not restate the wake model in the body. The body must not repeat the `watch` list, restate the `schedule`, or describe the daemon's activation mode as prose.
  4. Keep the body short and behavioral. Every body paragraph must change how the daemon decides, verifies, communicates, limits scope, coordinates, or no-ops. Remove generic filler, repeated policy, tutorial notes, and setup instructions.
  5. `purpose` must be one short outcome statement. It must describe the end state the daemon exists to maintain, not a list of scans, API calls, messages, implementation mechanics, historical context, activation details, deny rules, quiet/no-op policy, or other runtime guidance.
  6. Write `routines` as concrete wake-time operations. Each routine must be finite and verifiable. Advice belongs in body guidance; negative guardrails belong in `deny` or body policy.
  7. Use explicit, resolving references and destinations. Daemon IDs, docs paths, pinned refs, example names, issue or project identifiers, Slack channels, GitHub labels, and repo-specific configuration must be current and unambiguous.
## 2. Inspect every changed daemon file
  1. Reviewers must inspect the canonical `.agents/daemons//DAEMON.md` file.
  2. Reviewers must inspect changed `scripts/**` and `references/**` files.
  3. Reviewers must read nearby support files when the changed behavior depends on them and must flag stale, hidden, contradictory, or disconnected guidance.
  4. The PR must stay narrowly scoped to the requested daemon change; unrelated or unnecessary additions must be removed.
## 3. Review the `DAEMON.md` contract Use the [`DAEMON.md` reference](/daemons/daemon-md-reference) when you need the full authored contract. ### Required file shape
  1. `DAEMON.md` must live at `.agents/daemons//DAEMON.md`.
  2. `DAEMON.md` must start with `---`-delimited YAML frontmatter that parses to an object/map.
  3. `DAEMON.md` must have a non-empty Markdown body below the closing delimiter.
### Authored fields and structural rules
  1. The frontmatter must include `id`, `purpose`, and `routines`.
  2. `id` must exactly match the `` path slug.
  3. The frontmatter must include at least one activation field: `watch` or `schedule`.
  4. `routines` must be a YAML list with at least one non-empty string. When present, `watch` and `deny` must also be YAML lists of non-empty strings.
  5. The frontmatter must use the canonical authored field names and must not invent fields or substitute `name`, `description`, `triggers`, `actions`, or `disallowed` for `id`, `purpose`, `watch`, `routines`, or `deny`.
### Field quality
  1. `purpose` must be one short outcome statement. It must describe the end state the daemon exists to maintain, not how the daemon works, why it was created, when it wakes, when it stays silent, or what it must not do.
  2. routines must contain only concrete, finite, verifiable operations the daemon performs when it wakes. Advice belongs in the body; negative guardrails belong in deny or body policy.
  3. `deny` must cover plausible adjacent risky shortcuts and must fail closed for important ambiguity.
  4. `watch` must use semantic matching language and describe provider-visible observable events rather than raw webhook labels or derived-state queries that belong in execution-time checks.
  5. `watch` entries must not be vague, brittle, catch-all, or broadly overlapping in ways that can produce duplicate activations.
  6. When present, `schedule` must use standard five-field cron in UTC. If both day-of-month and day-of-week are restricted, reviewers must account for ordinary cron `day-of-month OR day-of-week` semantics.
### Validate with the CLI Run the daemon CLI from the repository that contains the changed daemon file to confirm the authored contract is valid. ```bash theme={null} bunx @charlie-labs/daemons validate .agents/daemons//DAEMON.md ``` Use `bunx @charlie-labs/daemons validate --all` when the PR changes multiple daemon files. ## 4. Review body guidance Use [Writing and editing `DAEMON.md`](/daemons/writing-and-editing-daemon-md) for deeper authoring guidance.
  1. The Markdown body must contain runtime policy rather than setup instructions or tutorials.
  2. The body must not duplicate frontmatter content. If body guidance restates `purpose`, `watch`, `routines`, `deny`, or `schedule`, reviewers must delete it or move only the missing hard rule into frontmatter.
  3. The body must add targeted guidance only where it materially improves behavior: decision terms, output format, communication and silent no-op policy, verification and freshness, limits, target selection and ignore patterns, coordination and concurrency, human authority, and clarifying examples.
  4. The body must define ambiguous terms once and must remove duplicative or overly verbose content. It must rely on frontmatter and linked canonical sources instead of restating them.
## 5. Review support files
  1. Reviewers must inspect the full daemon support tree when it changes.
```text theme={null} .agents/daemons// DAEMON.md scripts/ references/ ``` ### `scripts/`
  1. Scripts must be used for executable, deterministic helpers.
  2. Scripts must document required inputs, output shape, pagination or completeness expectations, and safe behavior when complete context is unavailable.
### `references/`
  1. References must be used for read-only policy, templates, guides, examples, or other context.
  2. `DAEMON.md` must say when and how to use any reference the daemon relies on.
## 6. Review repeatability and safety
  1. Wake context must be treated as activation context rather than mutation authorization. Current provider state must be refetched immediately before consequential writes.
  2. Limits must be added when work or visible noise can sprawl.
  3. For branch mutation, force-push must be denied by default. If repository policy permits it, the daemon must require fresh remote state and `--force-with-lease`. It must state whether fork or cross-repository branches are out of scope.
## Learn more * [Daemons](/daemons): concepts, wake model, and `DAEMON.md` as the control surface. * [Choosing daemons](/daemons/choosing-daemons): how to identify and scope daemon opportunities. * [Writing and editing `DAEMON.md`](/daemons/writing-and-editing-daemon-md): guidance for clear, narrow, safe daemon files. * [`DAEMON.md` reference](/daemons/daemon-md-reference): exact authored contract, validation rules, and support-tree semantics. * [Testing and iterating on daemons](/daemons/testing-and-iterating-on-daemons): testing, observation, and iteration guidance. # Testing and iterating on daemons Source: https://docs.charlielabs.ai/daemons/testing-and-iterating-on-daemons Roll out daemons safely, reduce blast radius, and iterate based on observed behavior. **Human readers:** the fastest way to get started with daemons is to work directly with Charlie. **Charlie can help you** suggest where to start and add your first daemon. Use the prompts below to ask Charlie in Slack, GitHub, or Linear. ```markdown Learn about daemons theme={null} Explain what daemons are and how they work. ``` ```markdown Daemon use cases theme={null} What are some use cases for daemons? Which would you recommend starting with? ``` ```markdown Add a daemon theme={null} Open a PR to add a simple daemon to my repo. Explain what the daemon does and why you think it's a good place to start. ``` *** Use this page when a daemon file already exists and you need to roll it out safely, observe its behavior, and tighten or widen it over time. ## Start with the rollout mindset Early testing is not about proving the daemon can do everything. Early testing is about proving the daemon behaves correctly and quietly under narrow, intentional conditions. Use your team’s normal activation workflow for rollout. A daemon becomes eligible for live activations after both are true: * the updated `DAEMON.md` is merged to the repo’s default branch * Charlie has ingested that merged version Once both are true, the daemon is live: * current `watch` conditions can begin matching * a current `schedule` can begin driving scheduled activations * the first question is not “can it do more?” but “can it behave correctly under narrow conditions?” Start narrow. Prefer low-blast-radius outputs first. Widen only after repeated correct behavior. Use the daemon file itself to enforce narrowness. Do not rely on people remembering to be careful during rollout. ## Keep the file model straight The authored frontmatter fields are: * `id` * `purpose` * `watch` * `routines` * `deny` * `schedule` `schedule` uses standard five-field cron syntax. The markdown body can include headings like Decision policy, Communication policy, Verification and freshness, Limits, Coordination, and Ignore patterns, but those headings are recommended conventions, not frontmatter schema. Activation-mode labels such as Watch-only, Schedule-only, and Hybrid are runtime-derived from `watch` and `schedule`. Authors do not write them as frontmatter fields. ## What you can observe today Today, there is no dedicated daemon activity or logs page in the Charlie dashboard. Observe daemon behavior on native surfaces: * GitHub * Slack * Linear Because there is no dedicated activity page today, human rollout verification usually means inspecting the daemon’s visible work on those systems. That is different from daemon-internal verification. If the daemon file says the daemon must run checks, tests, API probes, freshness checks, or other validation before a consequential write, confirm the daemon actually performed that verification before trusting the activation. ## Containment levers Use these authoring levers to reduce blast radius before rollout. ### Narrow `watch` Make `watch` conditions specific enough for early rollout without making them misleading as a durable contract. Examples of narrow testing patterns: * only react to PRs opened by the person creating and testing the daemon * only react to a labeled test PR * only react to a deliberately narrow category of change * only react to a surface where the tester can inspect every result closely Temporary overfitting is acceptable during rollout, such as limiting activation to a test label, test branch, or test author. Remove that overfitting before normal rollout unless it is part of the daemon’s real long-term role. ### Narrow target boundaries Limit where the daemon pays attention. Put hard target boundaries in `watch`, `routines`, or `deny` when possible; use body policy only for nuanced target-selection behavior. Examples: * only a specific directory or file family * only PRs targeting one branch or branch pattern * only one issue label or PR label category * only a sandbox repo for the first rollout ### Add Ignore patterns Skip noise early. Examples: * ignore bot-authored activity * ignore generated files * ignore surfaces or categories that would create noisy false positives during testing ### Add stronger `deny` If the daemon could take risky actions, deny them during early rollout and widen later only if needed. ### Add Limits Constrain the daemon’s output volume. Examples: * at most N items per activation * no more than N visible actions per day * stop producing new work when too much prior daemon work is still waiting on humans ### Add Coordination Coordination rules are a containment lever, not just a cleanup detail. Examples: * do not comment where a human review is already in progress * do not duplicate work another daemon already started * use a label or ownership convention so other daemons can filter early-rollout work ### Constrain output surfaces first Prefer outputs that only the tester or a very small audience will see first. Examples: * send Slack DMs to the daemon author first * comment only on a labeled test PR * keep early outputs on one low-blast-radius surface ### Constrain who or what the daemon touches first Keep the first target set intentionally small. Examples: * only act on PRs opened by the daemon author * only act in a sandbox repo first * only act on a test branch pattern first * only act on one issue label or PR label first ## Testing by activation type Event wakes respond to a triggering signal. Scheduled wakes survey their target set and prioritize within it. Hybrid daemons need both postures, so test them separately at first. ### Watch-driven daemons Use watch-driven testing when the daemon should react to a discrete event. Current rollout facts that matter: * event-driven wakes can come from routed GitHub, Linear, and Slack events once repo inference selects a repo with daemon inventory * Linear tests depend on a connected Linear workspace, the issue's Linear team mapping, and routing content that clearly contains the observable trigger; Slack tests depend on a connected Slack workspace and Slack workspace mapping * `watch` conditions are interpreted semantically, so concrete observable phrasing is more reliable than vague wording A good rollout pattern for a watch-driven daemon is: 1. make `watch` narrow 2. constrain target boundaries, Ignore patterns, and Coordination 3. add strong `deny` and Limits 4. create a small number of deliberate test events 5. inspect visible output on GitHub, Slack, or Linear 6. tighten or widen the file and repeat If the daemon is noisy, first ask: * did it wake for the wrong event? * or did it wake correctly and act too broadly once awake? Wrong wakes usually point to `watch`, target boundaries, or Ignore patterns. Wrong actions usually point to `routines`, `deny`, Limits, Coordination, or other body guidance. #### Linear testing recipe Use a Linear test when the daemon should respond to supported issue events. 1. Confirm the Linear integration is connected and the Linear team you will test in is mapped to the intended repo. 2. Pick one low-risk issue path to test. 3. Use concrete `watch` wording that tests a supported issue create or issue-comment create event rather than the repo/team mapping precondition, such as “A new Linear issue contains the exact phrase `daemon-test-12345`” or “A Linear issue comment contains the exact phrase `daemon-test-12345`.” 4. Trigger that supported low-risk event by creating the test issue or adding the unique phrase as a normal issue comment. Use Charlie mention or assignment only when intentionally testing direct-invocation routing. 5. Inspect the daemon’s visible output on Linear and any linked GitHub or Slack surface. 6. Confirm it did not act on unrelated issues outside the mapped team or daemon scope. #### Slack testing recipe Use a Slack test when the daemon should respond to channel, thread, or DM activity. 1. Confirm the Slack integration is connected and the Slack workspace is mapped to the intended repo. 2. Pick one low-risk channel, thread, or DM path to test. 3. Use concrete `watch` wording, such as “A Slack thread reply is added in the named support channel.” 4. Trigger one event: a mention, thread reply, broader channel message, or DM. For broad message wakes, narrow matching to intentional channels in `watch` wording or body policy. 5. Inspect the daemon’s visible output in Slack and any linked Linear or GitHub surface. 6. Confirm it did not respond to unrelated channel traffic. ### Schedule-driven daemons Use schedule-driven testing when the daemon should wake on time and survey what needs attention inside a defined scope. Current rollout facts that matter: * invalid cron strings are rejected when daemon config is refreshed * if a schedule update is invalid and a previous valid schedule already exists, Charlie keeps the previous valid schedule until the cron value is fixed * schedule-based activations do not replay a full backlog of missed ticks * after downtime, the scheduler catches up at most one missed tick, then continues from current time A good rollout pattern for a schedule-driven daemon is: 1. start with conservative body guidance and low-blast-radius outputs 2. add strong Limits 3. make target boundaries intentionally narrow 4. choose a schedule that lets the tester observe the first activations closely 5. inspect the resulting visible actions 6. widen gradually only after behavior is consistently correct If the daemon is too noisy on schedule: * tighten target boundaries * add or strengthen Limits * narrow the `routines` to the minimum correct work If the daemon is too passive on schedule: * inspect whether target boundaries are too narrow * inspect whether `deny` or Limits are over-constraining it * inspect whether `routines` describe the intended action clearly enough ### Hybrid daemons Use hybrid testing when the daemon needs both event-driven reaction and scheduled review. Do not test both wake paths broadly at once. Get one wake path behaving well first, then add the second. A good order is: 1. test the lower-blast-radius path first 2. keep the other path narrow or conservative 3. observe several correct activations 4. widen one dimension at a time ## What actually wakes daemons today Today: * event-driven wakes can come from routed GitHub, Linear, and Slack events after repo inference selects a repo with daemon inventory * watch matching is semantic * `schedule` drives scheduled activations * activation mode is derived from `watch` and `schedule`, not authored as its own field For safe rollout, the important distinction is whether the daemon woke because of a signal or because a schedule fired. ## How to verify whether a daemon is working Check the daemon against its own file. For each activation, ask: * Did it wake for the right reason? * Did it follow the daemon’s `purpose`? * Did it perform one or more of its `routines`? * Did it avoid anything in `deny`? * Did it follow the body guidance for decision policy, communication policy, verification, freshness, limits, coordination, and ignore patterns? * Did it run any daemon-internal verification required by its file? * Did it produce work on the right native surface? * Did it do the minimum correct work for this activation? Separate wake context from action scope. `watch` and `schedule` explain why the daemon woke now. They do not expand what the daemon is allowed to do. Permission still comes from the daemon’s `purpose`, `routines`, `deny`, and body guidance. Because there is no dedicated activity page today, human rollout verification usually means reading the daemon’s visible work on GitHub, Slack, and Linear. It should also include checking whether the daemon performed any internal verification its file required before acting. ## A simple iteration loop Use this loop: 1. narrow the daemon file before rollout 2. activate it through the team’s normal rollout workflow 3. observe a small number of live activations 4. inspect the visible outputs 5. decide whether the problem is: * wrong wake * wrong action * missing Limit * missing target boundary or Ignore patterns * missing Coordination rule * missing `deny` 6. edit the daemon file 7. merge or activate the tighter or wider version 8. repeat The main debugging surface is the daemon file itself. `DAEMON.md` is the daemon’s canonical operating brief and primary role policy. When the daemon behaves incorrectly, fix the authored policy and guidance rather than trying to prompt around the problem. ## What to change first when something goes wrong ### If the daemon wakes too often Look first at: * `watch` * `schedule` * target boundaries * Ignore patterns ### If the daemon wakes correctly but is too chatty Look first at: * `routines` * Limits * Communication policy * Coordination ### If the daemon takes actions it should not take Look first at: * `deny` * target boundaries * Decision policy ### If the daemon is too passive Look first at: * whether target boundaries are too narrow * whether `routines` are too weak or vague * whether `deny` rules are over-constraining it ## Widening scope safely Widen one dimension at a time. Examples: * from “only PRs opened by the tester” to “one small team’s PRs” * from tester-only Slack DMs to a small shared channel * from one labeled test PR to one label category * from a sandbox repo to one low-risk production repo area * from one test branch pattern to one broader branch target * from a narrow directory to a broader repo area * from a small activation volume to a larger one Do not widen scope, output audience, routine breadth, and schedule intensity all at once. There is no fixed required number of successful activations defined here. In practice, widen only after several quiet, correct, low-blast-radius activations. Signals that a daemon is still too broad or under-constrained include: * it comments where humans are already actively working * it repeats work another daemon already did * it produces more visible output than the team can review * it touches targets outside the intended early rollout set * it acts instead of stopping, commenting with the blocking reason, or asking for specific human input on ambiguous cases ## Dampening or stopping a noisy daemon If a daemon is producing noise, merge a more restrictive daemon file quickly. Typical dampening moves: * tighten `watch` * narrow target boundaries * add Ignore patterns * add or strengthen `deny` * add or strengthen Limits * add stronger Coordination rules * stop/no-op, comment with the blocking reason, or ask for specific human input instead of acting directly * reduce visible output breadth * route early outputs back to low-blast-radius surfaces For scheduled daemons, removing `schedule` is the normal way to stop future timed activations. In rare stale-state windows, that change may not take effect instantly, so confirm on native surfaces before assuming the schedule path is fully stopped. ## Testing checklist Before rollout or widening, run structural validation in the repository that contains the daemon files: ```bash theme={null} bunx @charlie-labs/daemons validate --all ``` This checks file structure only. It does not prove live daemon behavior, wake routing, integration access, output quality, or any daemon-internal verification the file asks Charlie to perform. Before widening a daemon, confirm: * `bunx @charlie-labs/daemons validate --all` passes * the daemon woke only in the situations you expected * overlapping `watch` entries did not cause duplicate activations for the same underlying signal * the daemon’s visible outputs were easy to review * the daemon followed its own purpose and routines * the daemon respected `deny` and Limits * the daemon respected target boundaries, Coordination, and Ignore patterns * the daemon re-checked current state before acting when state could have changed * the daemon ran any internal verification required by its file * when the daemon no-oped or completed a low-noise action silently, that silence was consistent with the daemon’s communication policy * the daemon did not create more work than the team could absorb * the next widening step is small and deliberate If any of those are not true, tighten the daemon and test again. # Writing and editing DAEMON.md Source: https://docs.charlielabs.ai/daemons/writing-and-editing-daemon-md Create and improve DAEMON.md files that are narrow, explicit, and safe to run repeatedly. **Human readers:** the fastest way to get started with daemons is to work directly with Charlie. **Charlie can help you** suggest where to start and add your first daemon. Use the prompts below to ask Charlie in Slack, GitHub, or Linear. ```markdown Learn about daemons theme={null} Explain what daemons are and how they work. ``` ```markdown Daemon use cases theme={null} What are some use cases for daemons? Which would you recommend starting with? ``` ```markdown Add a daemon theme={null} Open a PR to add a simple daemon to my repo. Explain what the daemon does and why you think it's a good place to start. ``` *** Use this page when Charlie or another agent is creating a new daemon file or improving an existing one. Use [DAEMON.md reference](/daemons/daemon-md-reference) for the exact contract. Use this page to make the file good, not just valid. For a focused PR-review pass before merge, use the [Review Checklist](/daemons/review-checklist). ## The default stance Write the narrowest daemon that can do the job. A good daemon file is: * narrow * explicit * concise * predictable * easy to review and iterate on Start with: * one purpose * usually 2–3 routines * clear deny guidance * only the body sections that materially improve behavior Do not write a broad, impressive-looking daemon that is hard to trust. ## Frontmatter vs body Use **frontmatter** for the concise declarative contract: * identity * purpose * wake conditions * core routines * hard prohibitions * schedule Use the **body** for operating guidance that makes behavior more consistent: * decision policy * verification and freshness policy * output format * communication behavior * limits * target-selection details that cannot fit cleanly in frontmatter * priority * thresholds * conventions * coordination and concurrency behavior * ignore patterns * examples Authors set `watch` and/or `schedule`. The runtime derives activation mode from those fields; do not add `activationMode` as a frontmatter field. A daemon must include at least one of `watch` or `schedule`. If something is a stable authored field, it belongs in frontmatter. If something is operating guidance or judgment-shaping prose, it belongs in the body. Do not duplicate `purpose`, `watch`, `routines`, or `deny` in body sections. If a body section only restates frontmatter, delete it or move the hard rule into frontmatter. Hard target boundaries usually belong in `watch`, `routines`, or `deny`. Use the body for nuanced target-selection policy, not for generic restatements of what the daemon is. ### Runtime policy, not setup notes The body should read correctly while the daemon is running. Avoid prose like “replace this with your default branch” or “configure this command before enabling” inside `DAEMON.md`; those are setup instructions, not runtime policy. ## Create mode When creating a new daemon: ### 1. Write the purpose as outcome, not mechanics A strong `purpose` says what the daemon exists to achieve. Good: * “Keeps repository docs current and easy to discover.” Weak: * “Scans for stale markdown files and suggests updates.” The daemon’s purpose should make sense as the role’s job statement. ### 2. Choose the wake model Use `watch` when the daemon should react to discrete events. Write `watch` with concrete event or signal language available when the daemon wakes. If the daemon needs a follow-up query or derived analysis to know whether action is needed, put that check in execution guidance instead of the wake condition. Use `schedule` when the daemon should wake on time and survey what needs attention. Use both only when the same daemon role genuinely needs both immediate reaction and periodic review. A daemon must include at least one of `watch` or `schedule`. ### 3. Write a small number of concrete routines Routines should be concrete, finite operations. Good routines are things you can tell whether the daemon did or did not do. Good: * “propose clearer PR titles and summaries” * “identify missing test evidence in PR descriptions” * “suggest focused follow-up tasks when context is incomplete” Weak: * “help with pull requests” * “improve code quality” * “manage the repo” As a default, start with 2–3 routines. ### 4. Add deny rules for adjacent risks The most important `deny` rules are not random safety slogans. They are the risky actions the daemon might reasonably attempt as a natural extension of its routines. Examples from the authoring guidance: * “Do not merge pull requests” * “Do not approve pull requests on behalf of humans” * “Do not push commits directly to protected branches” * “Do not delete files or directories” If a daemon can plausibly take a risky shortcut, decide explicitly whether to deny it. ### 5. Add body guidance only where it improves behavior The body is not decorative. Use it to make the daemon more predictable. A small daemon does not need every possible section. Add sections when they materially improve consistency, reduce noise, or make outputs easier to review. Good body guidance usually answers questions that frontmatter cannot answer on its own: * when to act and when to stop/no-op * what evidence is enough * what freshness checks are required before writes * what verification the daemon must run before consequential changes * when to comment, react, resolve, hide, edit, push, or stay silent * how to avoid duplicate work with humans or other daemon activations ## Edit mode Editing is not the same as creation. When editing an existing daemon: ### 1. Read before rewriting Inspect the current daemon file first. Preserve: * the daemon’s identity * the intended role * any guidance that is already working Do not rewrite a daemon from scratch unless the role itself is wrong or the file is too weak to repair incrementally. ### 2. Diagnose the failure mode Before changing the file, decide whether the problem is: * the daemon wakes for the wrong things * the daemon wakes correctly but acts too broadly * the daemon lacks enough operating guidance * the daemon is missing limits * the daemon is over-constrained and too passive Different problems require different edits. ### 3. Tighten vague or brittle watch conditions Turn state-based or catch-all watch entries into specific, observable events. Prefer edits like: * “when a pull request is opened” * “when a maintainer comments `/daemon help` in a PR” * “when files matching docs/\*\*/\*.md are changed” Over vague entries like: * “when the repo needs attention” * “when code changes” Keep the daemon’s role the same. Make the wake logic more precise, not broader. Do not make `watch` depend on state that is not available in the event or signal that wakes the daemon. If the daemon needs a follow-up query or derived analysis to know whether action is needed, wake on the closest observable event and inspect that state during execution. Avoid overlapping watch entries that usually describe the same underlying trigger. One inclusive, concrete watch condition is usually easier to route and less likely to create duplicate activations than several broad entries. ### 4. Narrow or split broad routines If one routine covers several jobs, split it. If a routine sounds like an abstract responsibility, rewrite it as a concrete operation. Prefer the smallest set of routines that still captures the daemon’s real job. ### 5. Add missing deny rules Look for risky shortcuts the daemon might naturally attempt as the fastest way to satisfy its routines. Add deny rules for those nearby risks rather than generic safety slogans. ### 6. Reduce noise without changing the daemon’s identity If the daemon is doing the right kind of work but too often, too broadly, or too noisily, do not change the `purpose` first. Instead, tighten the parts that bound behavior: * add `Limits` to control volume or pacing * add target boundaries or `Ignore patterns` to filter surfaces early * add `Coordination` to avoid overlap with humans or other daemon activations This keeps the daemon’s role intact while making repeated activations more trustworthy. ### 7. Prefer targeted improvement over full rewrite Prefer the smallest change that clearly improves behavior. Rewrite only when the file’s role definition is fundamentally wrong or the current file is too inconsistent to repair incrementally. ## How to write strong frontmatter ### `id` `id` must exactly match the daemon ID and path slug. Good: * `pr-check-repair` Bad: * `PR Check Repair` ### `purpose` Write the intended outcome, not the mechanics. Good: * “Keeps repository docs current and easy to discover.” Bad: * “Scans for stale markdown files and suggests updates.” ### `watch` Each watch entry should describe a specific, observable event. Good: * “when a pull request is opened” * “when a maintainer comments `/daemon help` in a PR” * “when files matching docs/\*\*/\*.md are changed” Provider-specific examples should name provider-visible events rather than raw webhook labels. Good: * “A GitHub pull request review is submitted on an open non-draft pull request.” * “A top-level GitHub PR comment is created on a pull request.” * “A GitHub pull request head commit changes on an open non-draft pull request.” * “A GitHub-visible check run, check suite, or commit status fails on a pull request.” * “A Linear issue is created.” * “A Linear issue comment is added on an issue.” * “A Slack message is posted in the named support channel.” * “A Slack thread reply is added in the named support channel.” Weak: * “PR activity happens.” * “pull\_request\_review\.submitted webhook received.” * “when code quality is low” * “when the repo needs attention” * “when code changes” Use watch entries that describe something the system can observe as an event. Watch matching is semantic rather than deterministic rule matching, so concrete observable phrasing is more reliable than vague state-based phrasing. Be specific, but not brittle. Avoid both “wake on everything” and conditions that overfit to one temporary actor, exact phrase, or implementation detail unless that detail is part of the daemon’s durable activation contract. Do not write watch conditions that require data unavailable in the event or signal that wakes the daemon. For example, if a desired state can only be known by querying an API or analyzing repo state, use `watch` for the closest observable event and put the state check in `routines`, `deny`, or body policy. Avoid overlapping watch entries that usually describe the same underlying trigger. Prefer one inclusive observable event over several broad entries that may create duplicate activations. Current watch behavior can use routed GitHub, Linear, and Slack events after routing selects a repo with daemon inventory. For Linear events, write `watch` entries that name observable issue or comment activity and make sure the issue's Linear team maps to the intended repo. For Slack events, name observable channel or thread activity and make sure the relevant Slack workspace maps to the intended repo. ### `routines` Each routine should be a concrete, finite operation. Good: * “propose clearer PR titles and summaries” * “identify missing test evidence in PR descriptions” * “suggest focused follow-up tasks when context is incomplete” Weak: * “help with pull requests” * “improve code quality” * “manage the repo” ### `deny` Write deny rules for the most important nearby risks. If a daemon might reasonably try a risky action because it seems like the fastest path to its goal, decide explicitly whether to deny that action. Deny rules are the right place for hard boundaries: targets the daemon must not touch, actions it must not take, and cases where it must stop/no-op. If author identity, target resource, issue ID, validity, ownership, permissions, or freshness cannot be established, prefer fail-closed behavior unless the daemon explicitly allows best-effort action. ### Activation and execution safety `watch` gets the daemon activated. It does not prove action is safe. Use `routines`, `deny`, and body policy to make the daemon re-check current truth before acting. For mutating daemons, state what must be re-fetched or re-validated immediately before writes, pushes, or external updates. For daemons that push to PR branches, explicitly state force-push policy. Deny force-push by default unless the repo's policy permits rebase-based repair; when allowed, require fresh remote state and `--force-with-lease`. Also state whether fork or cross-repository PR branches are out of scope. ### `schedule` Use `schedule` for timer-based wakes. Schedules use standard five-field cron in UTC. This is for time-based survey or follow-up work, not for describing event conditions. The runtime may derive activation-mode labels from `watch` and `schedule`. Do not write `activationMode` as a frontmatter field. Examples: * `0 9 * * *` — daily at 09:00 UTC * `0 */6 * * *` — every 6 hours * `30 14 * * 1-5` — weekdays at 14:30 UTC ## How to write strong body guidance Use only the sections that materially improve behavior. ### `Decision policy` Use when the daemon needs decision rules, standards, or team opinions about what good looks like. Prefer compact “act when / stop when” rules, allow-lists, thresholds, and action matrices. Use examples to clarify judgment, not as a substitute for policy. Define ambiguous terms once. If the daemon depends on concepts like “valid,” “fixed,” “duplicate,” “straightforward,” “non-human,” or “blocked,” make those terms explicit. ### `Output format` Use when the daemon produces comments, reports, or other structured visible output and you want consistency. ### `Communication policy` Use when the daemon may communicate or mutate visible state. Define exactly when it should comment, react, resolve, hide, edit, push, or stay silent. Separate routine no-ops from comment-worthy cases. No-op silently for stale triggers, already-handled work, unsupported states, missing context, or ambiguity. Comment only when human input is specifically needed or visible action needs explanation. ### `Verification and freshness` Use when the daemon performs consequential writes or relies on mutable external state. State what the daemon must re-fetch or re-validate before acting. State what verification it must run, and what it should do if verification cannot run, fails, or fails for unrelated reasons. ### `Limits` Use when the daemon could otherwise create too much output or work. Limits can control: * batch size * rate * pacing relative to human capacity ### `Target selection` Use when the daemon needs nuanced filtering that cannot fit cleanly in `watch`, `routines`, or `deny`. Prefer domain-specific headings when they are clearer, such as `Candidate discovery`, `Triage items`, `Issue inference`, or `Repair policy`. ### `Priority` Use when several valid targets may compete for the daemon’s attention. ### `Thresholds` Use when numeric or qualitative cutoffs should affect detection or action. ### `Conventions` Use when the team has norms the daemon should follow. ### `Coordination` Use when the daemon might overlap with human work or other daemon activations. Define this daemon’s own boundary. Avoid naming sibling daemons unless another daemon is part of the activation contract or a true authority source. ### `Human authority` Use when human-authored input should govern the daemon’s behavior. Say when human input is a source of truth, and say when human-authored input should not be modified, adjudicated, or acted on automatically. ### `Ignore patterns` Use when specific files, directories, labels, authors, or event patterns should be skipped entirely. ### `Examples` Use when the daemon’s job includes subjective judgment and concrete examples will improve consistency. Examples are secondary to policy. Add them only when they clarify judgment that rules cannot capture. ## What to include only when it matters Not every daemon needs every body section. Add these only when they materially improve behavior: * Add `Limits` when the daemon could otherwise create more work or noise than the team can absorb. * Add `Output format` when the daemon produces comments, reports, or other recurring visible output and consistency matters. * Add `Communication policy` when the daemon may make visible comments or state changes. * Add `Verification and freshness` when the daemon writes, pushes, or updates external state. * Add `Coordination` when overlap with humans or other daemon activations is plausible. * Add `Target selection` or `Ignore patterns` when the daemon listens to a high-volume surface or only part of the repo or workflow is relevant. * Add `Examples` only when the daemon’s job depends on subjective judgment that policy alone does not capture. ## Common mistakes These are the most common daemon-file mistakes: ### Vague watch conditions A watch entry must describe an event, not a general state. ### Brittle watch conditions A watch entry should not depend on data unavailable in the event or signal that wakes the daemon. Use execution-time checks for state that requires a follow-up query or derived analysis. ### Overlapping watch conditions Multiple broad watch entries for the same underlying trigger can cause duplicate activations. Prefer one inclusive watch condition when event families overlap. ### Unbounded routines If you cannot tell whether the daemon completed the routine, the routine is too vague. ### Missing deny rules If a risky nearby action is plausible, decide explicitly whether to deny it. ### Duplicating frontmatter in the body Do not add body sections that only restate `purpose`, `watch`, `routines`, or `deny`. ### Undefined decision terms If a daemon relies on terms like “valid,” “fixed,” “duplicate,” or “straightforward,” define them once. ### Missing freshness or verification policy If a daemon writes, pushes, resolves, edits, or updates external state, say what current state it must re-check and what verification it must run before acting. ### No limits where output can sprawl A daemon without limits can produce more work or noise than the team can absorb. ### Invented frontmatter fields Do not invent fields. Use the authored fields from [DAEMON.md reference](/daemons/daemon-md-reference). ### Treating body headings like schema Headings such as `Decision policy`, `Limits`, or `Target selection` belong in the markdown body, not as new frontmatter fields. ### Generic filler in the body If a section does not change what the daemon will actually do, tighten it or remove it. ### Stale terminology after edits After changing policy, search for old daemon names, old terms, duplicate constraints, and contradictory allow/deny language. ## Final consistency scan Before merging a daemon file, scan it once from top to bottom and check: * Frontmatter contains the durable contract, and the body does not repeat it. * `watch` describes observable wake events, not derived state that must be queried during execution. * Hard boundaries are in `deny`, `watch`, or `routines`; nuanced selection policy is in the body. * Decision terms are defined once and used consistently. * Mutating behavior has freshness and verification requirements. * Communication behavior says when to comment, react, resolve, hide, edit, push, or stay silent. * Stop/no-op behavior is explicit for ambiguous identity, target, ownership, permissions, freshness, or validity. * The file has no stale section names, duplicate constraints, or contradictory allow/deny language left over from earlier edits. ## Critique rubric Use this rubric when reviewing a draft daemon file. ### Purpose * Is the purpose written as outcome, not mechanics? * Is the role narrow enough to explain in one sentence? ### Wake logic * Are watch conditions actual events? * Are watch conditions specific without being brittle? * Do watch conditions avoid relying on unavailable derived state? * Do overlapping watch entries risk duplicate activations? * Does the schedule serve a real time-based need? * Is the daemon using both `watch` and `schedule` only when both are justified? * If the daemon is noisy, can the wake logic be made more specific without changing the role? ### Routines * Are routines concrete and finite? * Would a human be able to tell whether the daemon did them? * Does each routine describe one kind of operation, or should any be split? ### Constraints * Are the main adjacent risks denied? * Are limits present when the daemon could create noise? * Would target-selection policy, coordination, or ignore patterns make the daemon more predictable? * Does the daemon fail closed on important ambiguity? ### Body guidance * Does the body add real behavioral guidance? * Are the sections specific, or are they generic filler? * Are ambiguous terms defined? * Are freshness, verification, communication, and concurrency rules present when the daemon needs them? * Does the file avoid coupling to named sibling daemons unless they are a true authority or activation source? ### Overall * Is this the narrowest useful daemon? * If it is noisy, can you fix that with tighter watch logic, routines, or body guidance before changing the purpose? * Would you trust this file to be activated repeatedly? * Has the file been scanned for stale terms and duplicated or contradictory rules? If the answer is “no” to any of those, edit the file before rollout. ## Scaffold from an example with the CLI When an existing example is close to the role you need, use the daemon CLI to inspect it, preview the scaffold, then write the draft files. ```bash theme={null} bunx @charlie-labs/daemons show pr-metadata bunx @charlie-labs/daemons add pr-metadata --dry-run bunx @charlie-labs/daemons add pr-metadata ``` `show` surfaces the example’s readiness, integrations, support files, and required adaptation notes. Review those notes before rollout; examples are starting patterns, not turnkey policy. `add --dry-run` previews the files without writing them. `add` writes the scaffold under `.agents/daemons//`, including the example `DAEMON.md` and any catalog-listed support files. Generated files are drafts. Adapt the daemon to the repository’s real role, constraints, integrations, output surfaces, and rollout plan before relying on it. Scaffolding does not activate the daemon. The daemon becomes eligible for live activations only after the change is merged to the repository default branch and Charlie ingests that merged version. For reproducible scaffolding, pass the same `--ref ` to `show` and `add` so browsing and generated files come from the same catalog snapshot. ## How to use examples while writing or editing Start with the nearest example daemon from the [examples repo README](https://github.com/charlie-labs/daemons/blob/master/README.md). Use examples to copy: * structure * level of specificity * the kind of constraints that belong with a daemon role Do not copy: * irrelevant routines * irrelevant deny rules * irrelevant scope or limits * team-specific conventions that do not apply here A good example gives you the right shape. It does not remove the need to adapt the daemon to the repo’s real role. # Docs, but Faster Source: https://docs.charlielabs.ai/docs-but-faster These are the fastest ways to learn about Charlie: If your organization has Charlie installed, ask @CharlieHelps in GitHub or ask @Charlie in a connected Slack or Linear workspace. Charlie can also help check or draft daemon `watch` wording. Use the AI-enabled search bar at the top of this docs site. Paste this LLM‑friendly reference into your model of choice: (click here). GitHub uses the CharlieHelps account, so mentions look like `@CharlieHelps`. Linear and Slack use Charlie’s app identity, so mentions usually look like `@Charlie`. # Environment setup Source: https://docs.charlielabs.ai/environment-setup Prepare Charlie devboxes for repo-specific dependencies and system packages. Charlie works best when your repo can set up its own development environment from a fresh clone. Put setup where your repo already runs after dependency installation. For JavaScript and TypeScript repos, that usually means `postinstall`. Gate Charlie-only work with `IS_CHARLIE=1`, store secrets in dashboard repository environment variables, and use `AGENTS.md` to tell Charlie how to use the environment. Use this setup path for build, test, and tooling dependencies that can be installed noninteractively. Running services, such as Docker Compose stacks, background processes, or databases, should be documented in `AGENTS.md` and handled as a separate workflow. ## What Charlie sets up automatically Charlie creates repo-specific devbox blueprints so new runs start from a prepared environment instead of a blank machine. For most repos, Charlie will: * Clone the repository into the devbox. * Provide common tools and package categories: Git, Bash, Python, Node package managers, build tools such as `make`, `cmake`, and `pkg-config`, CLIs such as `jq`, `ripgrep`, `curl`, and `gh`, `mise`, and native headers used by frequent Node/image/database-client packages. * Detect JavaScript package managers and run dependency installation with the repo's normal lifecycle scripts. * Inject configured repository environment variables from the Charlie dashboard. * Set `IS_CHARLIE=1` during Charlie runs. Charlie does not try to guess every apt package your repo might need. If your tests, builds, or tools need extra system packages after dependencies install, declare them in your repo setup. ## Recommended pattern For JavaScript and TypeScript repos, use this shape for setup that can run after package-manager dependency installation has completed: Keep `package.json` small and route the real work to a script. Do not use this path for apt packages that are required by dependency install scripts or native addon builds; `postinstall` runs too late for those. ```json theme={null} { "scripts": { "postinstall": "bash scripts/setup-charlie-env.sh" } } ``` The script should exit quickly outside Charlie unless you intentionally support local use. ```bash theme={null} #!/usr/bin/env bash set -euo pipefail if [[ "${IS_CHARLIE:-}" != "1" ]]; then exit 0 fi sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ libpq-dev \ pkg-config ``` Design setup so it is safe to run more than once. * Use `--no-install-recommends` for apt packages. * Pass `DEBIAN_FRONTEND=noninteractive` on each `sudo apt-get` command. * Prefer commands that no-op when packages already exist. * Do not prompt for input. * Do not print secrets. ## Install system dependencies Use real apt commands for packages that must exist before tests or build tools run, as long as those packages are not required during dependency installation itself. Example: native database client headers and build metadata: ```bash theme={null} sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ libpq-dev \ pkg-config ``` Example: image processing dependencies used by common native modules: ```bash theme={null} sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ libvips-dev \ libcairo2-dev \ libpango1.0-dev ``` Example: command-line tools needed by tests: ```bash theme={null} sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ poppler-utils \ graphviz ``` Keep the list focused. If Charlie already has a tool installed, you do not need to reinstall it. If a package is needed while `bun install`, `pnpm install`, `yarn install`, or `npm install` is still running, do not rely on `postinstall`; ask Charlie for a pre-dependency-install blueprint update instead. If you want one setup script to work in Charlie and on developer laptops, consider using `mise` for user-level tools and runtimes. Charlie devboxes include `mise`, so a guarded setup script can run `mise install` for repo tools while keeping apt-only work behind the `IS_CHARLIE=1` guard or a separate opt-in local path. ## Non-JS repos `postinstall` is the main recipe for work that can happen after JavaScript dependency installation. Charlie runs package-manager installs first, so dependency install scripts and native addon builds may fail before the root `postinstall` script can install missing system packages. For non-JS repos, use the closest equivalent your team already trusts: * `make setup-charlie` * `just setup-charlie` * `scripts/setup-charlie-env.sh` * `mise install` plus a documented setup task Then document the command in `AGENTS.md` so Charlie knows to run it when preparing the repo or debugging environment failures. ## Tell Charlie how to use the environment Use `AGENTS.md` for durable instructions about setup, verification, and known caveats. ```md theme={null} ## Charlie environment - Repo setup that runs after dependency installation is handled by `postinstall`, which calls `scripts/setup-charlie-env.sh`. - The setup script installs Charlie-only system packages when `IS_CHARLIE=1`. - If tests fail because a system package is missing after dependencies are installed, update the setup script instead of installing packages ad hoc. - If dependency installation itself fails because a native package is missing, ask Charlie for a pre-dependency-install blueprint update. - Required secrets are configured as Charlie dashboard repository environment variables. Do not print them in logs. - Validate environment changes with `bun test` and the affected package checks. ``` Keep this short and operational. `AGENTS.md` should tell Charlie what to do, not repeat every package in the setup script. ## Secrets and environment variables Use dashboard repository environment variables for non-public keys, tokens, and runtime config needed during Charlie runs. Configure them under your repository settings in the Charlie dashboard. Good uses include: * Package registry tokens such as `NPM_TOKEN`. * API keys for test doubles or sandbox services. * Runtime config used by build or verification commands. Do not commit secrets to the repo, echo them from setup scripts, or include them in generated logs. If a setup command needs a secret, read it from the environment and fail with a generic message when it is missing. ## How blueprints work Blueprints are repo-specific prepared devbox images. They are similar to Docker images: Charlie builds them from your repo setup so future devboxes can start faster with dependencies already present. Charlie checks blueprints on repository pushes and rebuilds them when the existing blueprint is stale. Changes may not apply immediately, and existing devboxes may keep using the environment they already have. If you need a setup change available right away, contact Charlie and ask for an on-demand blueprint update. ## Troubleshooting ### `apt-get` prompts or hangs Pass `DEBIAN_FRONTEND=noninteractive` on the `sudo apt-get` command and use `-y`: ```bash theme={null} sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ``` ### A native dependency fails during package install `postinstall` cannot repair apt packages required by dependency install scripts or native addon builds, because the package-manager install may fail before `postinstall` runs. Ask Charlie for a pre-dependency-install blueprint update for those packages. ### Package install works once, then fails later Make the script idempotent. Check for files, directories, or commands before creating or installing them, and prefer package manager commands that tolerate already-installed packages. ### A package is missing in Charlie but installed locally If the package is needed after dependencies install, add it to the repo setup script and commit the change. If it is needed while dependencies install, ask Charlie for a pre-dependency-install blueprint update. Local machine state is not part of the blueprint unless the repo can recreate it. ### A change is not visible in a new Charlie run Blueprint updates may lag. Ask Charlie for an on-demand blueprint update if the new package is blocking work. ### Setup needs a running service Leave it out of this page's setup path for now. Document the need in `AGENTS.md` and ask Charlie how to handle that workflow separately. ## Complete example `package.json`: ```json theme={null} { "scripts": { "postinstall": "bash scripts/setup-charlie-env.sh" } } ``` `scripts/setup-charlie-env.sh`: ```bash theme={null} #!/usr/bin/env bash set -euo pipefail if [[ "${IS_CHARLIE:-}" != "1" ]]; then exit 0 fi sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ libpq-dev \ pkg-config \ poppler-utils if command -v mise >/dev/null 2>&1 && [[ -f .mise.toml ]]; then mise trust .mise.toml -y mise install fi ``` `AGENTS.md`: ```md theme={null} ## Charlie environment - Environment setup that can run after dependency installation runs from `postinstall` via `scripts/setup-charlie-env.sh`. - Charlie-only setup is guarded by `IS_CHARLIE=1`. - Repository secrets come from Charlie dashboard environment variables. Never print them. - If tests or builds fail after dependency installation because a package is missing, update `scripts/setup-charlie-env.sh` and re-run the affected verification command. - If dependency installation itself fails because a native package is missing, ask Charlie for a pre-dependency-install blueprint update. ``` # FAQ Source: https://docs.charlielabs.ai/faq Common questions about Charlie, including daemon behavior. ## Daemons FAQ A daemon is a repo-defined operating role for recurring work. It is not a long-running OS process. Each daemon is defined in a file at: .agents/daemons/\/DAEMON.md watch wakes a daemon from matching events. schedule wakes a daemon on a cron timetable. A daemon can use either one or both. Daemons can wake from scheduled cron wakes through schedule, plus routed GitHub, Linear, and Slack events through watch. Linear event wakes require the Linear integration to be connected and the issue's Linear team mapped to the intended repo. Slack event wakes require the Slack integration to be connected and the Slack workspace mapped to the intended repo. Cron schedules are evaluated in UTC. Yes. Activations are bounded runs with explicit start/end, tool limits, and normal permission constraints. Daemons can use prior activation history as context, but they do not run as a permanently active process. Use only the canonical fields: id, purpose, watch, routines, deny, and schedule. Do not invent alternate top-level schema keys for activation mode. ## General FAQ Charlie is strongest in TypeScript and supports many common languages/frameworks used in modern repositories. Small and medium PRs are typically reviewed in minutes. Complex PRs can take longer when deeper validation is needed. Check installation scope, invocation method, and permissions first. Then use the [Troubleshooting](/troubleshooting) checklist. Yes. Charlie is useful for both human-authored and agent-authored pull requests. Charlie can analyze images attached in supported surfaces and use them as task context. Email [support@charlielabs.ai](mailto:support@charlielabs.ai). # How It Works Source: https://docs.charlielabs.ai/how-it-works Charlie’s runtime model, execution flow, and operating boundaries. To learn more about how daemons work, please read [How Daemons Work](https://charlielabs.ai/how-it-works). To learn more about how Charlie works generally, please read about [CAOS, the Coding Agent Operating System](https://charlielabs.ai/blog/charlie-v2-introducing-the-coding-agent-operating-system-caos/). ## Multi-surface wake model Charlie can start work from routed events on the systems your team already uses: * GitHub events such as mentions, review requests, assignments, and PR activity * Linear events such as issue creation, issue comments of any kind on mapped issues, issue mentions, and issues assigned to Charlie * Slack events such as mentions, thread replies, channel messages, and DMs * daemon `schedule` entries for cron-based wakes For daemon `watch` conditions, GitHub events route through the repo-scoped GitHub App installation. Linear events require a connected Linear workspace and the event issue's Linear team mapped to the repo where the daemon lives. Slack events require a connected Slack workspace mapped to that repo. Use [Daemons](/daemons) for wake model details and [Integrations](/integrations) for setup. Charlie run lifecycle visual overview # Installation Source: https://docs.charlielabs.ai/installation Set up Charlie with GitHub, then connect optional integrations. Charlie has two offerings: * **Agents** for on-demand work (mentions, assignments, review requests) * **Daemons** for scheduled or watch-based automation You need the GitHub setup below before either one can run. ## Required setup Go to dashboard.charlielabs.ai and create your account. Use a valid team email so Charlie can map the right org/workspace access. During onboarding, authorize GitHub OAuth for the account you'll use to install and manage Charlie. This lets Charlie verify your access and keep repo-level permissions aligned with GitHub. Install CharlieCreates and scope access to the exact repositories where Charlie should run. You can start narrow and add more repositories later. Using Charlie on public/open-source repositories? Review Open Source first, then follow your org’s security policy for GitHub App scope and token handling. ## Optional but recommended integrations You can use Charlie with GitHub only, but most teams also connect: * [Linear](/integrations/linear) * [Slack](/integrations/slack) * [Sentry](/integrations/sentry) * [Vercel](/integrations/vercel) See the [Integrations](/integrations) hub for setup paths. If you want Linear events to wake daemon `watch` conditions, connect Linear and map the relevant Linear team(s) to the repo where the daemon lives. For Slack event wakes, connect Slack and map the Slack workspace to that repo. GitHub-only setup supports GitHub-routed events and scheduled daemon wakes, but not Linear or Slack event wakes. ## Daemons setup If you want scheduled or watch-driven automation, continue to [Daemons](/daemons). # GitHub Integration Source: https://docs.charlielabs.ai/integrations/github Connect GitHub so Charlie can review PRs, open PRs, and keep Linear and Slack in sync. Connecting GitHub is required for Charlie to operate. This core integration gives Charlie access to your codebase and development workflow—enabling him to review code, push commits, create pull requests, and collaborate in your repository just like a fellow developer. ## What Charlie can do With GitHub connected, Charlie works directly in your repositories and pull requests: * **Review and improve code changes.** Charlie automatically reviews pull requests (especially when they’re marked “Ready for Review”) and provides detailed feedback. Mention `@CharlieHelps, review this PR` or assign him the pull request — he will analyze the diff and comment with findings on potential bugs, performance issues, and best practices. * **Implement code changes on demand.** Assign Charlie to a GitHub issue or ask `@CharlieHelps, open a PR to fix this` — he will create a new branch, commit the changes, and open a pull request that addresses the issue. All commits and the PR will reference the relevant issue or task for traceability. * **Brainstorm and plan solutions.** In an issue or PR comment, ask Charlie for an implementation plan (for example, `@CharlieHelps, how should we fix this?`). He’ll outline a step-by-step solution with code snippets, covering edge cases and validation. * **Answer code questions in context.** Need an explanation of a piece of code or history? Mention Charlie in a comment (e.g. `@CharlieHelps, explain what this function does`) and he’ll pull context from the repository to provide a clear answer with references to the relevant files or commits. * **Keep tasks and code in sync.** Charlie automatically links pull requests and commit messages to related issues (or Linear tickets if you use Linear). This ensures nothing falls through the cracks — every PR Charlie opens is tied to a tracking issue, and he checks that all requirements are met during his code review. ## Pull request reviews Marking a PR as "Ready for review" or assigning the PR to `@CharlieHelps` will initiate a PR review. * Inline review comments (on a specific diff line): Charlie only acts if the comment mentions `@CharlieHelps`. If the inline comments are part of a pending (unsubmitted) review, Charlie will not see them until you click 'Submit review'. * Submitted multi-comment reviews: When you submit the review, Charlie will act if the review body or any of its inline comments mention `@CharlieHelps`. * Request changes behavior: * On PRs opened by Charlie: submitting a review with Request changes triggers Charlie to make edits even without a mention. * On PRs opened by humans: Request changes does not trigger Charlie. Mention `@CharlieHelps`, request a review from `@CharlieHelps`, or assign the PR to `@CharlieHelps` instead. Request changes is only actionable on PRs opened by Charlie. ## Working with Charlie in GitHub Issues Charlie is fully integrated into the GitHub Issues workflow. You can assign work, request implementation plans, or ask technical questions—without leaving GitHub. Just mention `@CharlieHelps` in an issue (in the initial description when you create it) or comment, and he’ll handle the rest. ### Opening a Pull Request from an Issue Charlie can take an issue from idea to implementation: * **Assign the issue to Charlie.** In the GitHub issue, assign @CharlieHelps as the assignee. Charlie will immediately start working on the issue, using the issue title and description as his primary specification. * **Request a PR directly from an issue comment.** In any comment, mention Charlie with a clear instruction. For example: `@CharlieHelps, open a PR to fix this.` — Charlie will create a new branch, implement the fix, and open a pull request referencing the issue. * **Link Sentry issues for deeper debugging.** If you have [Sentry](/integrations/sentry) connected, simply link a Sentry issue in your GitHub issue or PR description. Charlie will automatically pull in stack traces and error context from Sentry to better diagnose and resolve the problem. * **Track progress in real time.** Charlie posts a status comment when he starts, and keeps it updated as he works. * **Mention Charlie when creating the issue.** In the GitHub issue, mention @CharlieHelps when creating the issue and Charlie will immediately start working on the issue, using the issue title and description as his primary specification. ### Communicating and Collaborating Charlie supports a wide range of requests and questions inside issues: * **Request a technical plan.** Ask Charlie for a detailed implementation plan or fix proposal `@CharlieHelps, outline a step-by-step plan to solve this issue.` * **Research and brainstorm solutions.** Have Charlie gather external resources or best practices: `@CharlieHelps, research approaches to this problem and include links to relevant documentation.` * **Get instant answers.** Ask Charlie to explain code, dependencies, or past changes related to the issue: `@CharlieHelps, explain the impact of implementing this issue.` * **Keep everything transparent.** Every action and response Charlie takes is posted directly in the issue, so your team stays in the loop. Charlie’s responses in GitHub Issues are always context-aware—grounded in your codebase, change history, and issue comments. ## Quick Setup 1. **Sign up and complete onboarding**\ Go to [Installation](/installation), create your account, and follow the onboarding steps. 2. **Invite Charlie to your repository**\ Add `@CharlieHelps` as a collaborator to your GitHub repo or organization.\ See [Installation](/installation) for details. ## Troubleshooting If something doesn't work as expected: * **Charlie isn't responding on PRs** – Verify the **CharlieCreates** GitHub App is installed and enabled for the repository. * **Can't mention @CharlieHelps** – Make sure `@CharlieHelps` has been invited to the repo (or is a member of the organization). If invites are delayed due to org policies, contact support. * **Charlie can't push commits** – Ensure the app and/or user has write access to the target branch, or ask Charlie to open a PR from a new branch. For more help, see the general Troubleshooting guide at [troubleshooting](/troubleshooting). # Integrations Source: https://docs.charlielabs.ai/integrations/index Connect Charlie directly to tools or provide environment variables via the dashboard. There are two ways to extend what Charlie can access: ## 1) Direct integrations Connect the platforms Charlie should read from or write to: * [GitHub](/integrations/github) * [Linear](/integrations/linear) * [Slack](/integrations/slack) * [Sentry](/integrations/sentry) * [Vercel](/integrations/vercel) GitHub, Linear, and Slack are not only context and write surfaces. GitHub-routed events use the repo-scoped GitHub App installation; Linear routed events require a connected Linear workspace and the event issue's Linear team mapped to the right repo; Slack routed events require a connected Slack workspace mapped to the right repo. Use `schedule` for cron-based daemon wakes. ## 2) Repository environment variables Use the dashboard to define per-repository environment variables for build/test/tooling workflows: * Dashboard: dashboard.charlielabs.ai * Configure variables under your repo settings Use this for non-public keys, tokens, and runtime config needed during Charlie runs. ### Conditional setup for Charlie runs If your repo needs different setup when Charlie is running—for example, during blueprint, devbox, build, test, or tooling scripts—gate that branch with an environment variable such as `IS_CHARLIE=1`. For example, your script can check `IS_CHARLIE` before running Charlie-specific setup, while keeping secrets and runtime configuration in repository environment variables. For the full recipe, see [Environment setup](/environment-setup). # Linear Integration Source: https://docs.charlielabs.ai/integrations/linear Linking Linear workspaces allows Charlie to open PRs, respond to comments, and more directly from your Linear workspace. ## What Charlie can do Once connected, Charlie will be able to: * **Respond in-thread with answers and code.** Ask **@Charlie** follow-up questions or clarifications right on the issue. * **Enrich the ticket with deeper context.** `@Charlie, enrich this issue` pulls stack traces, touched files, recent commits, and related PRs. * **Draft a ready-made implementation plan.** `@Charlie, write an implementation plan` returns a step-by-step roadmap with code changes, tests, and rollout notes. * **Open a pull-request from the issue.** Mention `@Charlie, open a PR for this` **or assign the issue to him**—he spins up a branch, pushes commits, and opens a PR that references the ticket. * **Keep tracking in lockstep.** Commits, PRs, and issue states stay linked automatically, and Charlie surfaces missing requirements when reviewing the PR. * **Understand your team’s priorities.** Has a high‑level understanding of the initiatives and projects that are in flight. ## Daemons and Linear events After you connect a Linear workspace, issue events can wake daemon `watch` conditions when the issue's Linear team is mapped to the repo. Use this when issue hygiene should happen from Linear activity, not only when someone explicitly asks Charlie to help. Supported routed Linear event families include: * issue creation (`Issue.create`) * issue mentions * issue comments * issues assigned to Charlie Project and initiative activity comments are not generally supported routed daemon wakes yet. Issue updates, status changes, label changes, and arbitrary assignment changes are also not currently routed daemon wakes. Linear event wakes require the Linear integration to be connected and the issue's Linear team to be mapped to the intended repo. Mentions and assignment wakes are useful for direct asks. For daemons, prefer passive issue-hygiene workflows that start from issue creation, issue comments, or scheduled surveys without requiring someone to invoke Charlie. Good daemon patterns for Linear include: * triaging newly created issues into labels, owners, or reproduction requests * reviewing new issue comments for blockers, reproduction details, ownership changes, or priority changes * asking for missing details when an issue does not include enough context to act safely * keeping follow-up hygiene current by finding stale asks, unclear owners, or mismatched linked PR state Use provider-visible `watch` wording for issue-created or comment-created workflows, for example: ```yaml theme={null} watch: - A Linear issue is created. - A Linear issue comment is added on an issue. ``` For passive hygiene that is not tied to one creation event, use `schedule` and inspect Linear state during execution: ```yaml theme={null} schedule: "0 16 * * 1-5" ``` ## Quick Setup Follow these steps to connect your Linear workspace with Charlie: Navigate to [dashboard.charlielabs.ai/integrations](https://dashboard.charlielabs.ai/integrations) and log into your account. If you have multiple GitHub organizations, you'll be prompted to select your organization before accessing the integrations page. linear-dashboard Click **Connect Linear** to initiate the integration process. Follow the Linear OAuth connection flow to authorize Charlie to access your Linear workspace. After successfully linking your Linear workspace, you will be redirected back to the Charlie dashboard. Go to your organization settings, select the repository, then select the Linear team(s) to connect to that repository. OAuth is workspace-scoped; repository routing is team-specific. connect-linear-team # Sentry Integration Source: https://docs.charlielabs.ai/integrations/sentry Connecting Sentry allows Charlie to access issues and traces directly, enabling faster debugging and streamlined issue resolution. ## What Charlie can do Charlie doesn’t just listen for Sentry webhooks—he actively digs into any Sentry trace he sees in Linear, GitHub, or Slack: * **Inspect any Sentry trace in context.** Mention **@Charlie** on a Linear issue, GitHub issue/PR, or Slack message with a Sentry link and he fetches the stack trace, culprit commits, and frequency data. * **Enrich the bug report automatically.** Charlie attaches that Sentry context back to the Linear ticket so the whole story sits in one place. * **Generate a fix plan.** `@Charlie, draft a fix plan` outlines code edits, tests, and rollout steps ready for review. * **Open a PR and ship the patch.** Approve the plan or say `@Charlie, open the PR` and he spins up a branch, commits the fix, and opens a PR linked to both Linear and Sentry. ## Quick Setup Follow these steps to connect your Sentry organization with Charlie: Navigate to [dashboard.charlielabs.ai/integrations](https://dashboard.charlielabs.ai/integrations) and log into your account. If you have multiple GitHub organizations, you'll be prompted to select your organization before accessing the integrations page. sentry-dashboard Click **Add Sentry** to open the integration popup. This will start a four-step process: sentry-dashboard 1. **Generate and Enter Token**: Get your Sentry User Auth Token from [https://sentry.io/settings/account/api/auth-tokens](https://sentry.io/settings/account/api/auth-tokens) (must have at least **Read** permissions). sentry-dashboard 2. **Validate Token**: Click **Validate Token** - verify your token and retrieve your available Sentry organizations. 3. **Select Organization**: Choose your Sentry organization from the dropdown that appears after validation. 4. **Save**: Click **Save** to complete the setup. Your Sentry organization is now connected to your GitHub organization. ## Troubleshooting If you encounter issues during setup: * **Token validation fails**: Ensure your token is valid and has at least **Read** permissions * **No organizations appear**: Verify your token belongs to the correct Sentry account * **Connection issues**: Check that your Sentry organization is accessible # Slack Integration Source: https://docs.charlielabs.ai/integrations/slack Connecting Slack lets Charlie turn threads into actionable Linear tickets, surface git & Sentry context, and ship fixes—all without leaving the thread. ## What Charlie can do With Slack connected, Charlie becomes a teammate in every channel: * **Capture a bug or task as a Linear ticket.** Mention **@Charlie** (e.g., `@Charlie, create a Linear issue with this bug`) and he creates the ticket with the full conversation. * **Pull rich context to understand the problem.** Ask `@Charlie, did we change anything before _X_ that caused this?` or drop a Sentry link—Charlie surfaces relevant commits, stack traces, and root-cause details. * **Brainstorm and propose a fix plan.** `@Charlie, how should we fix this?` delivers approaches, edge cases, and sample code blocks. * **Open a PR straight from chat.** `@Charlie, open a PR to fix this` spins up a branch, commits the patch, and posts the GitHub PR back into the thread. * **Summarize the discussion.** `@Charlie, summary please` condenses the thread into a crisp recap with action items and owners. ## Daemons and Slack events Connected Slack workspaces can also wake daemon `watch` conditions. Use this when useful work starts in Slack and should be handled as a repeatable operating role. Supported routed Slack event families include: * mentions and thread replies * broader channel messages * DMs Slack event wakes require the Slack integration to be connected and the Slack workspace mapping to route Slack activity to the intended repo. Channel constraints are not integration mappings; broad Slack message wakes should be narrowed to intentional channels in daemon `watch` wording or body policy. Good daemon patterns for Slack include: * triaging bug reports or support requests that appear in a shared channel * summarizing a noisy thread into next actions or a Linear issue * producing recurring support summaries or channel reports * keeping Slack-to-issue handoffs clean by filing, linking, and following up on the right issue Use provider-visible `watch` wording, for example: ```yaml theme={null} watch: - A Slack message in the named support channel reports a bug for this repo. - A Slack thread reply is added in the named incident channel. - A Slack message mentions Charlie in the named support channel. - A Slack DM asks Charlie to summarize or file a repo-related issue. ``` ## Quick Setup Follow these steps to connect your Slack workspace with Charlie: Navigate to [dashboard.charlielabs.ai/integrations](https://dashboard.charlielabs.ai/integrations) and log in. If you belong to multiple GitHub organizations, you'll be prompted to choose the organization before accessing the integrations page. slack-dashboard Click **Connect Slack** to start the integration process. Follow the Slack OAuth flow to authorize Charlie for your workspace. After authorizing, you'll be redirected back to the Charlie dashboard. Go to your organization settings and select your desired repository to connect it with a Slack workspace. connect-slack-team # Vercel Integration Source: https://docs.charlielabs.ai/integrations/vercel Charlie ships via Vercel previews and production deploys out of the box - zero extra seats, tokens, or configuration. ## What Charlie can do * **Automatic preview URLs** - Every branch Charlie opens spins up a Vercel preview so you can click, test, and iterate instantly. * **Hands-free production deploys** - Merge the PR and Vercel ships to prod; Charlie never needs to join your Vercel team. * **One seat, one flow** - Charlie acts only through GitHub, so your Vercel billing and permissions stay exactly as they are. * **Tighter feedback loop** - Review the code, open the preview, ship the fix - all in one pass. ## Quick Setup Good news - there is nothing to configure. ### Ensure the Vercel GitHub App is installed If your repository already has the [Vercel GitHub App](https://github.com/apps/vercel) installed you are done - Charlie will automatically trigger builds and deployments whenever he pushes commits or opens a PR. No API tokens, seats, or additional permissions required. vercel-charlie # Open Source Source: https://docs.charlielabs.ai/open-source How to use Charlie on public repositories with maintainer-first controls. Charlie works on public repositories, but open-source usage has stricter safety controls than private repo usage. ## Maintainer-only invocation model In maintainer repositories, Charlie only accepts invocations that are safely attributable to maintainers. That means: * maintainers can invoke Charlie in maintainer repo context, * outside collaborators cannot invoke Charlie with maintainer privileges, * untrusted or ambiguous trigger paths are intentionally conservative. ## Scope and leakage risk Public + private context mixing can leak information across surfaces if scope is too broad. Recommended defaults: * limit the GitHub App to only the repositories where Charlie is needed, * avoid broad org-wide installs unless required, * keep sensitive private repositories out of the same install scope when possible. ## External collaborator behavior External contributors cannot invoke Charlie in maintainer repo context. If they want Charlie support, they should use their own fork or organization install where they control permissions and scope. ## Fork behavior * **Maintainer-initiated workflows** from the main repo context are supported. * **Machine-initiated automations** are conservative when maintainer linkage is missing or unsafe. This conservative model is intentional to reduce abuse and cross-context data risk in open-source environments. # PR reviews Source: https://docs.charlielabs.ai/pr-reviews How Charlie approaches pull request reviews. By default, Charlie reviews open pull requests unless you explicitly tell it not to. ## What to expect * Feedback is optimized for decisions, not noise. * Reviews focus on correctness, risk, regressions, and missing validation. * Charlie can review code written by humans and code written by other agents. ## Steering review behavior You can narrow or change review behavior with direct instructions, for example: * "Only review for security and data integrity risks." * "Skip style comments and focus on runtime behavior." * "Review only files under `packages/api/**`." # Skills Source: https://docs.charlielabs.ai/skills Define reusable task-shaped playbooks and invoke them on demand. Skills are reusable, task-shaped playbooks for work you run repeatedly. ## Required location Store each skill at: `.agents/skills//SKILL.md` Example: `.agents/skills/upgrade-deps/SKILL.md` ## Invocation Call a skill by name in your request: `$` Example: `$upgrade-deps` ## Minimal skill anatomy A strong `SKILL.md` should include: * **Goal**: what outcome the skill produces. * **Inputs**: what context or parameters it expects. * **Steps**: the execution sequence. * **Return shape**: what output format to return. * **Guardrails**: hard boundaries and safety constraints. ## Instructions vs. skills * Put durable repo policy in [AGENTS.md instructions](/AGENTS.md-instructions). * Put repeatable task workflows in skills. If something should apply to every run, it belongs in `AGENTS.md`. If it should apply only when explicitly requested, it belongs in a skill. # Troubleshooting Source: https://docs.charlielabs.ai/troubleshooting Fast triage for setup, trigger, and run issues. Use this page for fast triage when Charlie is not responding or output doesn’t match expectations. ## Fast triage checklist 1. Confirm the repo is in scope for your GitHub App installation. 2. Confirm the triggering user has the required permissions on that surface. 3. Confirm Charlie was explicitly invoked (mention/review request/assignment) when needed. 4. Confirm required integrations are connected for the workflow you expect. 5. For Linear workflows, confirm the issue's Linear team is mapped to the intended repo. For Slack workflows, confirm the Slack workspace is mapped to the intended repo. 6. Re-run the request with a concrete, scoped instruction. For daemon-specific rollout/debugging, use [Testing and iterating on daemons](/daemons/testing-and-iterating-on-daemons). ## Common issues → next action ### Run failed mid-task * Read the failure message in the thread. * Re-trigger with explicit continuation context (what failed + what to try next). * If failures repeat, include exact command output in your follow-up. ### Charlie not visible in Linear assignee/search * Invite Charlie to the Linear workspace and any Linear teams where you expect him to appear. * Recheck the relevant Linear team mapping in your integration settings. ### No response on GitHub * Verify `CharlieCreates` is installed on that repo. * Confirm the event is one Charlie responds to (mention, review request, assignment, eligible PR event). * Confirm the sender has sufficient permissions for that repo context. ### No response on Linear * Confirm the Linear integration is connected. * Confirm the issue's Linear team is mapped to the intended repo. * For a direct request, use `@Charlie` in a supported issue comment, or assign the issue to Charlie when assignment is the intended action. * For a daemon wake, confirm the new issue or issue comment matches the daemon `watch`; issue comments do not need to mention Charlie. * Confirm the sender can access both the Linear issue and the mapped repo context. ### No response on Slack * Confirm the Slack integration is connected. * Confirm the Slack workspace is mapped to the intended repo. * Confirm Charlie can access the channel, thread, or DM where the request was made. * Use `@Charlie` for direct asks in Slack. For daemon watches on broader channel messages, make sure daemon `watch` wording or body policy narrows the wake to intentional channels. ### Daemon didn’t wake from a Linear or Slack event * Confirm the daemon file is merged on the repo default branch and has been ingested by Charlie. * For Linear, confirm the integration is connected and the issue's Linear team maps to the intended repo. For Slack, confirm the integration is connected and the Slack workspace mapping points to the intended repo. * Confirm the event is supported context: Linear issue creation, issue comments on mapped issues, including comments that do not mention Charlie, issue mentions, and issues assigned to Charlie; or Slack mentions/thread replies, channel messages, and DMs. * Confirm the triggering user and Charlie have permission to read the source issue, channel, thread, or DM. * Make the `watch` entry concrete and provider-visible, such as “A Linear issue is created” or “A Linear issue comment is added on an issue.” ### Mention doesn’t trigger * Use `@CharlieHelps` exactly in a supported comment/review context. * If using review comments, ensure it’s in a submitted review context when required. ### Charlie says it committed, but no commit appears * Check branch protection and required status rules. * Check whether local commit hooks or verification steps failed before push. * Ask Charlie to report branch name + exact failed command output. ### CI mismatch (Charlie passed locally, CI failed) * Compare environment variables and runtime versions between devbox and CI. * Ask Charlie to reproduce using the same script target CI runs. ### External CI logs (non-GitHub Actions) * Paste failing log excerpts or attach machine-readable test artifacts in-thread. * Ask Charlie to diagnose from that output and propose/apply a fix. ## Escalation checklist If you contact support, include: * **Where it was triggered** (GitHub/Linear/Slack + link) * **The failing message or error text** * **What you already tried** (steps + outcomes) Send details to [support@charlielabs.ai](mailto:support@charlielabs.ai).