1680 lines
59 KiB
Markdown
1680 lines
59 KiB
Markdown
# OPENCLAW_TRICKS.md — Configuration Recipes for OpenClaw Agents
|
||
|
||
A collection of tested patterns, prompts, and file structures for configuring an
|
||
OpenClaw agent as a proactive personal assistant. These are extracted from a
|
||
production setup that's been running since early 2026.
|
||
|
||
---
|
||
|
||
## How This System Evolved
|
||
|
||
This section explains what we built, why, and how the pieces fit together. The
|
||
rest of the document has the specific prompts and schemas — this part gives you
|
||
the big picture so you understand the design decisions.
|
||
|
||
### The Starting Point
|
||
|
||
OpenClaw gives you a persistent workspace directory and the ability to run tools
|
||
(shell, web, browser, messaging). Out of the box, the agent wakes up fresh every
|
||
session with no memory of what happened before. The core challenge is: **how do
|
||
you turn a stateless LLM into a stateful personal assistant that knows who you
|
||
are, where you are, what you need, and what it was working on?**
|
||
|
||
The answer is files. The workspace IS the agent's brain. Every piece of state,
|
||
every rule, every memory lives in files that the agent reads on startup and
|
||
updates as things change.
|
||
|
||
### The Workspace Files — Separation of Concerns
|
||
|
||
We started with one big AGENTS.md that had everything: rules, procedures,
|
||
medication instructions, git workflow, travel preferences. It worked at first
|
||
but quickly became unwieldy. The context window was getting eaten by
|
||
instructions the agent didn't need for most tasks.
|
||
|
||
The solution was **factoring out rulesets into focused files:**
|
||
|
||
- **AGENTS.md** — the master file. Responsibilities, session startup procedure,
|
||
high-level rules. Think of it as the table of contents that points to
|
||
everything else.
|
||
- **SOUL.md** — personality, tone, values. Separated because it's philosophical,
|
||
not procedural. Also: it's fun to let the agent evolve this one over time.
|
||
- **USER.md** — info about the human. Timezone, communication preferences,
|
||
basics the agent needs every session.
|
||
- **MEMORY.md** — curated long-term memory. Only loaded in private sessions
|
||
(security — prevents leaking personal context in group chats).
|
||
- **HEARTBEAT.md** — what to check on periodic heartbeat polls. Kept small
|
||
intentionally to minimize token burn on frequent checks.
|
||
- **TOOLS.md** — environment-specific notes (hostnames, device names, channel
|
||
IDs). Separated from skills because this is YOUR setup, skills are shared.
|
||
|
||
Then the procedural rulesets got their own files too:
|
||
|
||
- **`memory/medications-instructions.md`** — full medication protocol. Dosages,
|
||
schedules, safety rules, overdose prevention logic. Only loaded when
|
||
medication tasks come up.
|
||
- **`memory/checklist-pr.md`** — PR quality gate. What to verify before pushing,
|
||
after sub-agent pushes, before assigning to the human for review.
|
||
- **`memory/checklist-flights.md`** — flight time verification, prep block
|
||
creation, landing checklist triggers.
|
||
- **`memory/checklist-medications.md`** — pre-action verification before
|
||
reporting medication status or sending reminders.
|
||
- **`memory/checklist-messaging.md`** — rules for every outgoing message: URL
|
||
verification, timezone conversion, status claim verification.
|
||
- **`memory/landing-checklist.md`** — post-flight procedures (update location,
|
||
timezone, check meds, sync calendar).
|
||
|
||
The key insight: **MEMORY.md has a "checklists" section at the very top** that
|
||
says "before doing X, read file Y." The agent reads MEMORY.md at session start,
|
||
sees the checklist index, and knows which file to consult before any action.
|
||
This way the detailed instructions aren't always in context — they're loaded
|
||
on-demand.
|
||
|
||
```markdown
|
||
## ⛔ CHECKLISTS (read the relevant file before acting)
|
||
|
||
- **Any PR/code work** → `memory/checklist-pr.md`
|
||
- **Any message to the user** → `memory/checklist-messaging.md`
|
||
- **Medications** → `memory/checklist-medications.md`
|
||
- **Flights/travel** → `memory/checklist-flights.md`
|
||
```
|
||
|
||
### The Daily Context State File
|
||
|
||
This was probably the single most impactful addition. It's a JSON file
|
||
(`memory/daily-context.json`) that every session reads on every message. It
|
||
tracks the current state of the human: where they are, what timezone they're in,
|
||
whether they're sleeping, when they last took meds, when they last sent a
|
||
message.
|
||
|
||
Why JSON instead of markdown? Because it's machine-readable. The agent can
|
||
update individual fields programmatically, and the structure is unambiguous.
|
||
Markdown is great for instructions; JSON is great for state.
|
||
|
||
The daily context file means:
|
||
|
||
- The agent always knows the human's timezone (critical for a frequent traveler)
|
||
- Medication reminders fire based on actual timestamps, not guesses
|
||
- Sleep predictions inform when to send alerts vs stay quiet
|
||
- Location tracking happens automatically
|
||
|
||
### The Two-Tier Memory System
|
||
|
||
Daily files (`memory/YYYY-MM-DD.md`) are raw logs — what happened each day.
|
||
Topic tables, decisions, lessons, notes. Think of these as a daily journal.
|
||
|
||
MEMORY.md is curated long-term memory — the distilled essence. Standing rules,
|
||
key facts, lessons learned. Think of this as what a human would "just know"
|
||
about their life.
|
||
|
||
During heartbeats, the agent periodically reviews recent daily files and
|
||
promotes significant items to MEMORY.md, and removes stale info. It's explicitly
|
||
modeled on how human memory works: raw experience gets processed into lasting
|
||
knowledge, and irrelevant details fade.
|
||
|
||
The security model: MEMORY.md is only loaded in private (1:1 DM) sessions. In
|
||
group chats, the agent works from daily files and daily-context.json only. This
|
||
prevents personal context from leaking into shared channels.
|
||
|
||
### Medication Tracking
|
||
|
||
This is a safety-critical system. The design is deliberately paranoid:
|
||
|
||
- **CSV as source of truth.** Not the daily-context boolean, not the agent's
|
||
memory — the CSV log file is authoritative.
|
||
- **Double-verification before any action.** The daily context has a
|
||
`hasTakenDailyMedsToday` boolean AND a `dailyMedsTimestamp`. A midnight cron
|
||
resets the boolean. But if the human is in a timezone ahead of the server, the
|
||
reset happens at the wrong time. So the rule is: always verify the timestamp
|
||
falls on today's date in the human's timezone. The boolean is a convenience
|
||
hint, never the source of truth.
|
||
- **Interval medications anchored to actual doses.** Some meds are "every 5
|
||
days" or "every 14 days." The next dose date is calculated from the last
|
||
actual dose timestamp, not from the intended schedule. If a dose is missed,
|
||
the next date is unknown until the missed dose is taken and logged.
|
||
- **Overdose prevention.** The agent blocks logging if it detects a same-day
|
||
duplicate batch. This is the highest-priority safety rule — a duplicate daily
|
||
batch of certain medications could cause cardiac arrest.
|
||
- **Escalating alerts.** If daily meds are >26h since last dose, the agent
|
||
escalates aggressively — ntfy push notification if chat messages go
|
||
unacknowledged.
|
||
|
||
The medication instructions file is loaded on-demand (not every session),
|
||
keeping context costs low for non-medication conversations.
|
||
|
||
### Sleep Tracking
|
||
|
||
The agent infers sleep from activity gaps rather than requiring explicit "I'm
|
||
going to sleep" statements. On every message, it checks: was there a gap since
|
||
the last message that overlaps with the predicted sleep window? If so, log sleep
|
||
start = last activity before gap, wake = first activity after.
|
||
|
||
Activity isn't just chat messages — it includes Gitea commits, comments,
|
||
scheduled flight departures, and any other observable actions.
|
||
|
||
Sleep predictions drift over time (the human in this setup tends to sleep ~30min
|
||
later each day), so the agent tracks the trend and extrapolates. Caffeine intake
|
||
adjusts predictions forward. The predictions feed into:
|
||
|
||
- When to send alerts vs stay quiet
|
||
- Sitrep "sleep conflict" warnings (appointment during predicted sleep)
|
||
- General awareness of the human's schedule
|
||
|
||
### Location & Timezone Tracking
|
||
|
||
The agent always knows where the human is. This matters because:
|
||
|
||
- Times must always be displayed in the human's local timezone, not the server's
|
||
timezone
|
||
- "Today" and "tomorrow" mean different things in different timezones
|
||
- Medication timing needs to account for timezone changes
|
||
- Flight prep needs local airport info
|
||
|
||
The landing checklist (triggered automatically after every flight) updates
|
||
location, timezone, nearest airport, and lodging in the daily context file. It
|
||
also checks if any cron jobs have hardcoded timezones that need updating.
|
||
|
||
### The Gitea Notification Poller
|
||
|
||
OpenClaw has heartbeats, but those are periodic (every ~30min). For Gitea issues
|
||
and PRs, we wanted near-realtime response. The solution: a tiny Python script
|
||
that polls the Gitea notifications API every 2 seconds and wakes the agent via
|
||
OpenClaw's `/hooks/wake` endpoint when new notifications arrive.
|
||
|
||
Key design decisions:
|
||
|
||
- **The poller never marks notifications as read.** That's the agent's job after
|
||
it processes them. This prevents the poller and agent from racing.
|
||
- **It tracks notification IDs, not counts.** This way it only fires on
|
||
genuinely new notifications, not re-reads of existing ones.
|
||
- **The wake message tells the agent to route output to Gitea/Mattermost, not to
|
||
DM.** This prevents chatty notification processing from disturbing the human.
|
||
- **Zero dependencies.** Just Python stdlib (`urllib`, `json`, `time`). Runs
|
||
anywhere.
|
||
|
||
Here's the full source:
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""
|
||
Gitea notification poller.
|
||
Polls for unread notifications and wakes OpenClaw when the count
|
||
changes. The AGENT marks notifications as read after processing —
|
||
the poller never marks anything as read.
|
||
|
||
Required env vars:
|
||
GITEA_URL - Gitea instance URL
|
||
GITEA_TOKEN - Gitea API token
|
||
HOOK_TOKEN - OpenClaw hooks auth token
|
||
|
||
Optional env vars:
|
||
GATEWAY_URL - OpenClaw gateway URL (default: http://127.0.0.1:18789)
|
||
POLL_DELAY - Delay between polls in seconds (default: 2)
|
||
"""
|
||
|
||
import json
|
||
import os
|
||
import sys
|
||
import time
|
||
import urllib.request
|
||
import urllib.error
|
||
|
||
GITEA_URL = os.environ.get("GITEA_URL", "").rstrip("/")
|
||
GITEA_TOKEN = os.environ.get("GITEA_TOKEN", "")
|
||
GATEWAY_URL = os.environ.get("GATEWAY_URL", "http://127.0.0.1:18789").rstrip(
|
||
"/"
|
||
)
|
||
HOOK_TOKEN = os.environ.get("HOOK_TOKEN", "")
|
||
POLL_DELAY = int(os.environ.get("POLL_DELAY", "2"))
|
||
|
||
|
||
def check_config():
|
||
missing = []
|
||
if not GITEA_URL:
|
||
missing.append("GITEA_URL")
|
||
if not GITEA_TOKEN:
|
||
missing.append("GITEA_TOKEN")
|
||
if not HOOK_TOKEN:
|
||
missing.append("HOOK_TOKEN")
|
||
if missing:
|
||
print(
|
||
f"ERROR: Missing required env vars: {', '.join(missing)}",
|
||
file=sys.stderr,
|
||
)
|
||
sys.exit(1)
|
||
|
||
|
||
def gitea_unread_ids():
|
||
"""Return set of unread notification IDs."""
|
||
req = urllib.request.Request(
|
||
f"{GITEA_URL}/api/v1/notifications?status-types=unread",
|
||
headers={"Authorization": f"token {GITEA_TOKEN}"},
|
||
)
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||
notifs = json.loads(resp.read())
|
||
return {n["id"] for n in notifs}
|
||
except Exception as e:
|
||
print(
|
||
f"WARN: Gitea API failed: {e}", file=sys.stderr, flush=True
|
||
)
|
||
return set()
|
||
|
||
|
||
def wake_openclaw(count):
|
||
text = (
|
||
f"[Gitea Notification] {count} new notification(s). "
|
||
"Check your Gitea notification inbox via API, process them, "
|
||
"and mark as read when done. "
|
||
"Route all output to Gitea comments or Mattermost #git/#claw. "
|
||
"Do NOT reply to this session — respond with NO_REPLY."
|
||
)
|
||
payload = json.dumps({"text": text, "mode": "now"}).encode()
|
||
req = urllib.request.Request(
|
||
f"{GATEWAY_URL}/hooks/wake",
|
||
data=payload,
|
||
headers={
|
||
"Authorization": f"Bearer {HOOK_TOKEN}",
|
||
"Content-Type": "application/json",
|
||
},
|
||
method="POST",
|
||
)
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=5) as resp:
|
||
status = resp.status
|
||
print(f" Wake responded: {status}", flush=True)
|
||
return True
|
||
except Exception as e:
|
||
print(
|
||
f"WARN: Failed to wake OpenClaw: {e}",
|
||
file=sys.stderr,
|
||
flush=True,
|
||
)
|
||
return False
|
||
|
||
|
||
def main():
|
||
check_config()
|
||
print(
|
||
f"Gitea notification poller started (delay={POLL_DELAY}s)",
|
||
flush=True,
|
||
)
|
||
|
||
last_seen_ids = gitea_unread_ids()
|
||
print(
|
||
f"Initial unread: {len(last_seen_ids)} notification(s)", flush=True
|
||
)
|
||
|
||
while True:
|
||
time.sleep(POLL_DELAY)
|
||
|
||
current_ids = gitea_unread_ids()
|
||
new_ids = current_ids - last_seen_ids
|
||
|
||
if not new_ids:
|
||
last_seen_ids = current_ids
|
||
continue
|
||
|
||
ts = time.strftime("%H:%M:%S")
|
||
print(
|
||
f"[{ts}] {len(new_ids)} new notification(s) "
|
||
f"({len(current_ids)} total unread), waking agent",
|
||
flush=True,
|
||
)
|
||
|
||
wake_openclaw(len(new_ids))
|
||
last_seen_ids = current_ids
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|
||
```
|
||
|
||
Run it as a background service (launchd on macOS, systemd on Linux) with the env
|
||
vars set. It's intentionally simple — no frameworks, no async, no dependencies.
|
||
|
||
### The Daily Diary
|
||
|
||
Every day gets a `memory/YYYY-MM-DD.md` file. The agent appends a topic summary
|
||
to a table after every meaningful conversation:
|
||
|
||
```markdown
|
||
## Topics
|
||
|
||
| Time (TZ) | Topic |
|
||
| --------- | ------------------------------------------------ |
|
||
| 14:30 | Discussed PR review workflow |
|
||
| 16:00 | Medication logged |
|
||
| 18:45 | Flight prep blocks created for tomorrow's flight |
|
||
```
|
||
|
||
This serves multiple purposes:
|
||
|
||
- Any session can see what's been discussed today without loading full
|
||
conversation history
|
||
- The agent can use `sessions_history` to get details on a specific topic if
|
||
needed
|
||
- During memory maintenance, the agent reviews these to decide what's worth
|
||
promoting to MEMORY.md
|
||
- It's a simple audit trail of what happened
|
||
|
||
### The Requirement Capture Rule
|
||
|
||
One of the most important rules: **every single requirement, preference, or
|
||
instruction the human provides MUST be captured in a file immediately.** Not
|
||
"mentally noted" — written to disk. Because the agent wakes up fresh every
|
||
session, a "mental note" is worthless. If it's not in a file, it didn't happen.
|
||
|
||
This applies to everything: project rules ("no mocks in tests"), workflow
|
||
preferences ("fewer PRs, don't over-split"), corrections, new policies.
|
||
Immediate write to the daily file, and to MEMORY.md if it's a standing rule.
|
||
|
||
### PII-Aware Output Routing
|
||
|
||
A lesson learned the hard way: **the audience determines what you can say, not
|
||
who asked.** If the human asks for a medication status report in a group
|
||
channel, the agent can't just dump it there — other people can read it. The
|
||
rule: if the output would contain PII and the channel isn't private, redirect to
|
||
DM and reply in-channel with "sent privately."
|
||
|
||
This is enforced at multiple levels:
|
||
|
||
- AGENTS.md has a warning banner at the top
|
||
- The checklist system catches it before action
|
||
- Channel-specific rule files (like our `memory/JAMES_CHAT.md`) define what's
|
||
shareable per-channel
|
||
|
||
### Sub-Agent Isolation
|
||
|
||
When spawning coding sub-agents for PR work, each one MUST clone to a fresh
|
||
temporary directory. Never share git clones between agents — dirty working
|
||
directories cause false CI results, merge conflicts, and wrong file state. The
|
||
rule: `cd $(mktemp -d) && git clone <url> . && ...`
|
||
|
||
### The Heartbeat System
|
||
|
||
OpenClaw polls the agent periodically with a configurable prompt. The agent
|
||
reads HEARTBEAT.md and decides what to do. We keep HEARTBEAT.md small (focused
|
||
checklist) to minimize token burn on these frequent checks.
|
||
|
||
The heartbeat handles:
|
||
|
||
- Gitea inbox triage (check for new assignments)
|
||
- Flight prep block creation (look ahead 7 days)
|
||
- Open project review (can I take a step on anything?)
|
||
- Workspace sync (commit and push changes)
|
||
- Periodic memory maintenance
|
||
|
||
State tracking in `memory/heartbeat-state.json` prevents redundant checks (e.g.,
|
||
don't re-check email if you checked 10 minutes ago).
|
||
|
||
The key output rule: heartbeats should either be `HEARTBEAT_OK` (nothing to do)
|
||
or a direct alert. Work narration goes to a designated status channel, never to
|
||
the human's DM.
|
||
|
||
### The Workspace as a Git Repo (Disaster Recovery + Debugging)
|
||
|
||
The entire workspace is itself a git repo, aggressively committed and pushed
|
||
after every change. A cron job runs every 6 hours as a safety net, but the agent
|
||
pushes promptly after any modification.
|
||
|
||
This gives you three things:
|
||
|
||
1. **Disaster recovery.** The git remote is on a different machine (ideally in a
|
||
different building/city/country from the OpenClaw host). If the host dies,
|
||
you clone the workspace onto a new machine and the agent picks up where it
|
||
left off — all memory files, daily context, logs, checklists intact.
|
||
|
||
2. **State snapshots.** Every commit is a snapshot of the agent's complete state
|
||
at that moment. If the agent corrupts a file, overwrites something important,
|
||
or makes a bad edit, you can `git log` and `git checkout` to recover any
|
||
previous version.
|
||
|
||
3. **Prompt debugging.** When the agent behaves unexpectedly, you can look at
|
||
the git history to see exactly what its state files contained at the time.
|
||
What did daily-context.json say? What was in MEMORY.md? What checklist
|
||
version was it using? This turns "why did it do that?" from guesswork into
|
||
forensics.
|
||
|
||
The agent treats this repo differently from code repos — no PRs, no branches,
|
||
just direct commits to main. It's a state mirror, not a development workflow.
|
||
|
||
```markdown
|
||
### State Repo (clawstate.git)
|
||
|
||
- Commit and push workspace changes after any change to workspace files
|
||
- Push promptly — the remote repo should reflect the workspace in effectively
|
||
realtime
|
||
- Auto-sync cron runs every 6h as a safety net
|
||
```
|
||
|
||
**Important:** Code repos should be cloned OUTSIDE the workspace (e.g.
|
||
`~/repos/` or a fast external drive) to avoid embedding git repos inside the
|
||
workspace repo. The workspace repo tracks workspace files only.
|
||
|
||
**A deliberate policy exception:** The workspace repo violates one of the most
|
||
important repo policies — "never commit secrets." The workspace contains API
|
||
keys, tokens, and credentials in files like TOOLS.md because those are part of
|
||
the agent's operational state. This is an accepted exception because the repo is
|
||
permanently private and serves as a backup/DR system, not a development repo. If
|
||
your workspace state repo were ever to become public, it would be a catastrophic
|
||
leak. Treat it accordingly: private visibility, restricted access, no forks.
|
||
|
||
### Git Quality Standards — Interlocking Automated Checks
|
||
|
||
An AI agent will forget things. It will skip the formatter, push without
|
||
testing, weaken a linter rule to make CI pass, or use `git add .` and commit
|
||
junk files. You cannot rely on "be careful" — you need automated gates that make
|
||
it structurally impossible to ship bad code.
|
||
|
||
The approach we use is based on
|
||
[sneak/prompts](https://git.eeqj.de/sneak/prompts), a repo of standardized
|
||
policies that every project copies. The key document is
|
||
[REPO_POLICIES.md](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md),
|
||
which defines the interlocking check system.
|
||
|
||
#### The Interlocking Chain
|
||
|
||
The checks form a dependency chain where each layer requires the previous:
|
||
|
||
```
|
||
Gitea Actions CI
|
||
└── docker build .
|
||
└── make check (inside Dockerfile)
|
||
├── make fmt-check (code formatting)
|
||
├── make lint (static analysis)
|
||
└── make test (unit/integration tests)
|
||
```
|
||
|
||
This means:
|
||
|
||
- **CI runs `docker build .`** — that's it, one command
|
||
- **The Dockerfile runs `make check`** as a build step — if checks fail, the
|
||
Docker build fails, CI fails
|
||
- **`make check` depends on `fmt-check`, `lint`, and `test`** — all three must
|
||
pass
|
||
- **You can't skip any layer** — they're structurally linked
|
||
|
||
From
|
||
[REPO_POLICIES.md](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md):
|
||
|
||
> Every repo with software must have a root `Makefile` with these targets:
|
||
> `make test`, `make lint`, `make fmt` (writes), `make fmt-check` (read-only),
|
||
> `make check` (prereqs: `test`, `lint`, `fmt-check`), `make docker`, and
|
||
> `make hooks` (installs pre-commit hook).
|
||
|
||
> Every repo should have a `Dockerfile`. All Dockerfiles must run `make check`
|
||
> as a build step so the build fails if the branch is not green.
|
||
|
||
> Every repo should have a Gitea Actions workflow (`.gitea/workflows/`) that
|
||
> runs `docker build .` on push. Since the Dockerfile already runs `make check`,
|
||
> a successful build implies all checks pass.
|
||
|
||
#### Why Docker as the CI Runner
|
||
|
||
Running `make check` inside `docker build` solves the "works on my machine"
|
||
problem:
|
||
|
||
- **Clean environment every time.** No stale caches, no leftover files, no wrong
|
||
toolchain version
|
||
- **Reproducible.** The Dockerfile pins the base image by SHA (not tag), so the
|
||
build environment is identical everywhere
|
||
- **No CI configuration drift.** The CI workflow is one line: `docker build .`.
|
||
All the actual logic lives in the Dockerfile and Makefile, which are
|
||
version-controlled in the repo
|
||
|
||
From REPO_POLICIES.md:
|
||
|
||
> ALL external references must be pinned by cryptographic hash. This includes
|
||
> Docker base images, Go modules, npm packages, GitHub Actions, and anything
|
||
> else fetched from a remote source. Version tags (`@v4`, `@latest`, `:3.21`,
|
||
> etc.) are server-mutable and therefore remote code execution vulnerabilities.
|
||
|
||
#### The Makefile as Authoritative Documentation
|
||
|
||
The Makefile isn't just a build tool — it's the single source of truth for how
|
||
the repo works:
|
||
|
||
> The Makefile is authoritative documentation for how the repo is used. Beyond
|
||
> the required targets above, it should have targets for every common operation:
|
||
> running a local development server (`make run`, `make dev`), re-initializing
|
||
> or migrating the database (`make db-reset`, `make migrate`), building
|
||
> artifacts (`make build`), generating code, seeding data, or anything else a
|
||
> developer would do regularly. If someone checks out the repo and types
|
||
> `make<tab>`, they should see every meaningful operation available.
|
||
|
||
This isn't an AI-specific pattern — it predates agents entirely. A Makefile in
|
||
every repo has always been good engineering practice because it gives any new
|
||
developer (human or AI) a plain, direct, authoritative reference for how to
|
||
interact with the repo. It's authoritative _because it's how we actually invoke
|
||
the tools_ — it's not documentation that can drift from reality, it IS the
|
||
reality. `make<tab>` shows you everything you can do. No wikis to read, no
|
||
READMEs to hope are up-to-date.
|
||
|
||
For AI agents specifically, this is even more valuable:
|
||
|
||
- The agent always uses `make test`, never `go test ./...` directly — the
|
||
Makefile encapsulates flags, timeouts, environment setup
|
||
- A new sub-agent spawned on any repo can immediately see every available
|
||
operation without reading docs or asking questions
|
||
- The human never has to explain "how to run the tests" — it's always
|
||
`make test`, everywhere, every repo
|
||
|
||
#### Pre-Commit Hooks
|
||
|
||
Local enforcement before code even reaches the remote:
|
||
|
||
> Pre-commit hook: `make check` if local testing is possible, otherwise
|
||
> `make lint && make fmt-check`. The Makefile should provide a `make hooks`
|
||
> target to install the pre-commit hook.
|
||
|
||
Our PR checklist requires agents to install hooks after every clone:
|
||
|
||
```sh
|
||
echo '#!/bin/sh\nmake check' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
|
||
```
|
||
|
||
#### Linter Config Is Sacred
|
||
|
||
One of the most dangerous failure modes (documented in the failures section
|
||
above) is an agent modifying linter config to make checks pass:
|
||
|
||
> `.golangci.yml` is standardized and must _NEVER_ be modified by an agent, only
|
||
> manually by the user.
|
||
|
||
This is enforced in our PR checklist:
|
||
|
||
```markdown
|
||
## After sub-agent pushes code
|
||
|
||
1. Check diff for .golangci.yml / linter / test config changes
|
||
2. Check diff for `-short` / `-timeout` flags added to test commands
|
||
3. If any config files changed: reject and rework
|
||
```
|
||
|
||
The principle: **if the check fails, fix the code, not the check.** This applies
|
||
universally — linter rules, test assertions, formatting config, CI workflows.
|
||
Weakening a gate to make it pass is worse than a loud failure, because loud
|
||
failures get fixed while silent rot compounds.
|
||
|
||
#### Formatting Standards
|
||
|
||
From
|
||
[REPO_POLICIES.md](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md):
|
||
|
||
> Use platform-standard formatters: `black` for Python, `prettier` for
|
||
> JS/CSS/Markdown/HTML, `go fmt` for Go. Always use default configuration with
|
||
> two exceptions: four-space indents (except Go), and `proseWrap: always` for
|
||
> Markdown (hard-wrap at 80 columns).
|
||
|
||
This means formatting is never a judgment call — run the formatter, done. The
|
||
`make fmt` target writes changes, `make fmt-check` verifies without modifying
|
||
(used in CI). Agents run `make fmt` before committing, CI runs `make fmt-check`
|
||
to catch anything that slipped through.
|
||
|
||
#### Test Requirements
|
||
|
||
> All repos with software must have tests that run via the platform-standard
|
||
> test framework (`go test`, `pytest`, `jest`/`vitest`, etc.). If no meaningful
|
||
> tests exist yet, add the most minimal test possible — e.g. importing the
|
||
> module under test to verify it compiles/parses. There is no excuse for
|
||
> `make test` to be a no-op.
|
||
|
||
> `make test` must complete in under 20 seconds. Add a 30-second timeout in the
|
||
> Makefile.
|
||
|
||
The timeout is critical for agents — without it, a hanging test blocks the
|
||
entire sub-agent session. If tests take too long, that's a bug to file, not a
|
||
timeout to increase.
|
||
|
||
#### Git Hygiene Rules
|
||
|
||
From REPO_POLICIES.md and our operational experience:
|
||
|
||
- **Never `git add -A` or `git add .`** — always stage files explicitly. Agents
|
||
love to `git add .` and accidentally commit `.DS_Store`, editor swap files, or
|
||
debug output
|
||
- **Never force-push to main** — feature branches only
|
||
- **Each change = separate commit** — formatting changes go in their own commit
|
||
before logic changes
|
||
- **Rebase before and after** — PRs must be mergeable at time of push
|
||
- **Never commit secrets** — `.env`, credentials, API keys in `.gitignore`
|
||
|
||
#### The PR Pipeline
|
||
|
||
Our agent follows a strict PR lifecycle:
|
||
|
||
```markdown
|
||
## PR pipeline (every PR, no exceptions)
|
||
|
||
1. **Review/rework loop**: code review → rework → re-review → repeat until clean
|
||
2. **Check/rework loop**: `make check` + `docker build .` → rework → re-check →
|
||
repeat until clean
|
||
3. Only after BOTH loops pass with zero issues: assign to human
|
||
|
||
- "Passes checks" ≠ "ready for human"
|
||
- Never weaken tests/linters. Fix the code.
|
||
- Pre-existing failures are YOUR problem. Fix them as part of your PR.
|
||
```
|
||
|
||
The agent doesn't just create a PR and hand it off — it drives the PR through
|
||
review, rework, and verification until it's genuinely ready. A PR assigned to
|
||
the human means: all checks pass, code reviewed, review feedback addressed,
|
||
rebased against main, no conflicts. Anything less is the agent's open task.
|
||
|
||
#### New Repo Bootstrap
|
||
|
||
Every new repo follows a checklist from REPO_POLICIES.md:
|
||
|
||
> New repos must contain at minimum: `README.md`, `.git`, `.gitignore`,
|
||
> `.editorconfig`, `LICENSE`, `REPO_POLICIES.md`, `Makefile`, `Dockerfile`,
|
||
> `.dockerignore`, `.gitea/workflows/check.yml`
|
||
|
||
Plus language-specific files (Go: `go.mod`, `.golangci.yml`; JS: `package.json`,
|
||
`.prettierrc`; Python: `pyproject.toml`).
|
||
|
||
The full standardized configs are available at
|
||
`https://git.eeqj.de/sneak/prompts/raw/branch/main/<filename>` — agents fetch
|
||
them when bootstrapping a new repo, ensuring consistency across all projects.
|
||
|
||
#### Why This Matters for AI Agents Specifically
|
||
|
||
AI agents have a unique failure mode: they're confidently wrong. An agent will
|
||
push code that "should work," assert that checks pass without running them, or
|
||
silently weaken a gate to make the build green. Automated interlocking checks
|
||
turn these soft failures into hard failures:
|
||
|
||
- Can't push unformatted code → `make fmt-check` in pre-commit hook
|
||
- Can't skip tests → `make check` depends on `make test`
|
||
- Can't weaken linters → config file changes flagged in PR review
|
||
- Can't claim "CI passes" without proof → Docker build is pass/fail
|
||
- Can't ship without the human seeing it → PR assignment rules
|
||
|
||
The agent doesn't need willpower or attention to detail. It needs a system where
|
||
doing the wrong thing fails loudly.
|
||
|
||
#### Checklists Over Prose — Why Redundancy Is the Point
|
||
|
||
REPO_POLICIES.md describes everything: what files are required, how the Makefile
|
||
should work, how formatting works, how CI works. It's comprehensive. But
|
||
comprehensive prose doesn't keep an agent on track — **point-by-point checklists
|
||
do.**
|
||
|
||
That's why we also maintain two separate checklists alongside REPO_POLICIES.md:
|
||
|
||
- [**NEW_REPO_CHECKLIST.md**](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/NEW_REPO_CHECKLIST.md)
|
||
— step-by-step when creating a repo from scratch
|
||
- [**EXISTING_REPO_CHECKLIST.md**](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/EXISTING_REPO_CHECKLIST.md)
|
||
— step-by-step when starting work in a repo that may not conform yet
|
||
|
||
These are intentionally redundant with REPO_POLICIES.md. That's the point.
|
||
|
||
**Why redundancy works for AI agents:**
|
||
|
||
LLMs are good at linear, sequential processing. Give them a prose document with
|
||
30 requirements scattered across 10 paragraphs, and they'll miss things. Give
|
||
them a numbered checklist where each item is a concrete action with a checkbox,
|
||
and they'll march through it reliably.
|
||
|
||
The
|
||
[NEW_REPO_CHECKLIST.md](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/NEW_REPO_CHECKLIST.md)
|
||
walks through repo creation in five phases:
|
||
|
||
```markdown
|
||
# 1. Initialize
|
||
|
||
- [ ] `git init`
|
||
- [ ] Ask the user for the license
|
||
|
||
# 2. First Commit (README only)
|
||
|
||
- [ ] Create README.md with all required sections
|
||
- [ ] `git add README.md && git commit`
|
||
|
||
# 3. Scaffolding (feature branch)
|
||
|
||
- [ ] Fetch .gitignore, .editorconfig from templates
|
||
- [ ] Create LICENSE, REPO_POLICIES.md, Dockerfile
|
||
- [ ] Configure Makefile with all required targets
|
||
|
||
# 4. Verify
|
||
|
||
- [ ] `make check` passes
|
||
- [ ] `make docker` succeeds
|
||
- [ ] No secrets in repo
|
||
|
||
# 5. Merge and Set Up
|
||
|
||
- [ ] Merge to main, install hooks, push
|
||
```
|
||
|
||
The
|
||
[EXISTING_REPO_CHECKLIST.md](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/EXISTING_REPO_CHECKLIST.md)
|
||
does the same for existing repos — check each item, fix gaps before starting
|
||
your actual task:
|
||
|
||
```markdown
|
||
# Formatting (do this first)
|
||
|
||
- [ ] Run `make fmt` as standalone commit before any other changes
|
||
|
||
# Required Files
|
||
|
||
- [ ] README.md, LICENSE, REPO_POLICIES.md, .gitignore, .editorconfig
|
||
- [ ] Dockerfile, .dockerignore, Gitea Actions workflow
|
||
|
||
# Makefile
|
||
|
||
- [ ] All required targets exist and work
|
||
- [ ] `make check` passes
|
||
|
||
# Git Hygiene
|
||
|
||
- [ ] Pre-commit hook installed, no secrets, all refs pinned
|
||
|
||
# Final
|
||
|
||
- [ ] `make check` passes, `docker build` succeeds
|
||
- [ ] Fix everything before starting your actual task
|
||
```
|
||
|
||
**The same principle applies everywhere in the agent's configuration.** We have
|
||
checklists for:
|
||
|
||
- **PR quality gates** (`memory/checklist-pr.md`) — what to verify before
|
||
pushing, after sub-agent pushes, before assigning to the human
|
||
- **Medication actions** (`memory/checklist-medications.md`) — what to verify
|
||
before reporting status or sending reminders
|
||
- **Flight actions** (`memory/checklist-flights.md`) — what to verify before
|
||
stating flight times
|
||
- **Messaging** (`memory/checklist-messaging.md`) — what to verify before
|
||
sending any message (URLs resolve? times converted? status verified?)
|
||
|
||
Each checklist is loaded on-demand (referenced from a checklist index at the top
|
||
of MEMORY.md). The agent reads the relevant checklist before the relevant
|
||
action. It's the same pattern as REPO_POLICIES + NEW_REPO_CHECKLIST: prose
|
||
document for understanding, checklist for execution.
|
||
|
||
**The meta-lesson: AI agents are linear thinkers.** They follow step-by-step
|
||
instructions reliably. They follow scattered prose requirements unreliably.
|
||
Structure your rules as checklists, accept the redundancy, and treat it as a
|
||
feature — not a code smell.
|
||
|
||
### Putting It All Together
|
||
|
||
The system works as a loop:
|
||
|
||
1. **Session starts** → read SOUL, USER, daily-context, today's daily file
|
||
2. **Message arrives** → check daily-context for state changes (sleep gap?
|
||
location change? meds overdue?), respond to the message, update state
|
||
3. **Heartbeat fires** → check inbox, projects, flights, sync workspace
|
||
4. **External event** (Gitea poller wake) → process notification, respond via
|
||
appropriate channel
|
||
5. **Session ends** → state persists in files for next session
|
||
|
||
The files are the continuity. The agent is stateless; the workspace is not.
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
1. [Workspace Bootstrapping](#workspace-bootstrapping)
|
||
2. [Daily Context — The State File](#daily-context--the-state-file)
|
||
3. [Sitrep (Situation Report)](#sitrep-situation-report)
|
||
4. [Sleep Tracking](#sleep-tracking)
|
||
5. [Location & Timezone Tracking](#location--timezone-tracking)
|
||
6. [Medication Tracking](#medication-tracking)
|
||
7. [Flight & Travel Logging](#flight--travel-logging)
|
||
8. [Landing Checklist](#landing-checklist)
|
||
9. [Memory Architecture](#memory-architecture)
|
||
10. [Heartbeat Configuration](#heartbeat-configuration)
|
||
11. [Gitea Integration & Notification Polling](#gitea-integration--notification-polling)
|
||
12. [Sub-Agent Management](#sub-agent-management)
|
||
13. [Urgent Notifications via ntfy](#urgent-notifications-via-ntfy)
|
||
14. [Group Chat Behavior](#group-chat-behavior)
|
||
15. [Cron vs Heartbeat — When to Use Each](#cron-vs-heartbeat)
|
||
16. [Requirement Capture](#requirement-capture)
|
||
|
||
---
|
||
|
||
## Workspace Bootstrapping
|
||
|
||
Your workspace is the agent's home directory. Core files:
|
||
|
||
```
|
||
workspace/
|
||
├── AGENTS.md # Responsibilities, rules, procedures
|
||
├── SOUL.md # Personality, tone, values
|
||
├── USER.md # Info about your human (name, tz, prefs)
|
||
├── IDENTITY.md # Agent's own identity
|
||
├── HEARTBEAT.md # What to check on heartbeat polls
|
||
├── MEMORY.md # Curated long-term memory
|
||
├── TOOLS.md # Environment-specific notes (hosts, keys, devices)
|
||
└── memory/
|
||
├── daily-context.json
|
||
├── YYYY-MM-DD.md # Daily raw notes
|
||
├── sleep-log.csv
|
||
├── location-log.csv
|
||
├── flight-log.csv
|
||
├── medications-log-YYYY-MM.csv
|
||
├── medications-instructions.md
|
||
├── landing-checklist.md
|
||
├── checklist-*.md # Various operational checklists
|
||
└── heartbeat-state.json
|
||
```
|
||
|
||
### Session startup prompt (put in AGENTS.md):
|
||
|
||
```markdown
|
||
## Every Session
|
||
|
||
Before doing anything else:
|
||
|
||
1. Read `SOUL.md` — this is who you are
|
||
2. Read `USER.md` — this is who you're helping
|
||
3. Read `memory/daily-context.json` — current state (location, timezone, sleep,
|
||
meds)
|
||
4. Read `memory/YYYY-MM-DD.md` (today + yesterday) for recent context
|
||
5. **If in MAIN SESSION** (direct chat with your human): Also read `MEMORY.md`
|
||
|
||
Don't ask permission. Just do it.
|
||
```
|
||
|
||
This ensures the agent always has context on wake. The key insight is that the
|
||
agent wakes up fresh every session — files ARE its memory.
|
||
|
||
---
|
||
|
||
## Daily Context — The State File
|
||
|
||
This is the single most important file. It's a JSON blob that every session
|
||
reads on every message. It tracks the current state of your human.
|
||
|
||
### Schema: `memory/daily-context.json`
|
||
|
||
```json
|
||
{
|
||
"isSleeping": false,
|
||
"lastKnownWakeTime": "2026-02-28T12:30:00+07:00",
|
||
"predictedSleepTime": "2026-03-01T05:30:00+07:00",
|
||
"predictedWakeTime": "2026-03-01T12:45:00+07:00",
|
||
"hasTakenDailyMedsToday": false,
|
||
"dailyMedsTimestamp": "2026-02-27T11:43:00+07:00",
|
||
"lastCaffeineTimestamp": null,
|
||
"currentLocation": "City (IATA)",
|
||
"currentTimezone": "Asia/Bangkok",
|
||
"currentLodging": "Hotel Name",
|
||
"travelLeg": "Description of current travel phase",
|
||
"nearestAirport": "ICAO code",
|
||
"lastMessageFromUser": "2026-02-28T05:20:00+07:00",
|
||
"lastMessageChannel": "mattermost",
|
||
"isAvailable": true,
|
||
"lastUpdated": "2026-02-28T06:00:00+07:00",
|
||
"lastSleep": "actual: 2026-02-28 05:15-12:30 ICT (~7h15m). Stable pattern."
|
||
}
|
||
```
|
||
|
||
### AGENTS.md prompt:
|
||
|
||
```markdown
|
||
## Daily Context
|
||
|
||
Stored in `memory/daily-context.json`. All agents: review on every
|
||
conversation/message to detect if any state has changed. Update the JSON file
|
||
directly.
|
||
```
|
||
|
||
### Why it works:
|
||
|
||
- Every session reads this first, so the agent always knows where you are
|
||
- Sleep predictions help the agent decide when to alert vs stay quiet
|
||
- Medication timestamps enable overdue detection
|
||
- Timezone field means the agent always converts times correctly
|
||
- `lastMessageFromUser` lets the agent infer activity gaps = sleep
|
||
|
||
---
|
||
|
||
## Sitrep (Situation Report)
|
||
|
||
The sitrep is triggered by a keyword (e.g. "sitrep") and produces a concise
|
||
status briefing. The key design feature is that the agent can **add sections
|
||
dynamically** based on what it thinks you should know.
|
||
|
||
### AGENTS.md prompt:
|
||
|
||
```markdown
|
||
### Sitrep (Situation Report)
|
||
|
||
When the user says "sitrep", provide a concise summary covering:
|
||
|
||
1. **Daily meds** — taken today or not
|
||
2. **Interval meds** — any due/overdue or upcoming in next 48h. ALWAYS include
|
||
"X days from now" countdown. ALWAYS include day of week.
|
||
3. **Open issues assigned to me** — count
|
||
4. **Open issues assigned to user** — count + brief summary
|
||
5. **Upcoming travel** — flights in next 72h with times/airports. ALWAYS also
|
||
include the next known flight even if beyond 72h.
|
||
6. **Upcoming appointments** — next 48h
|
||
7. **Todo list** — overdue, due today, due tomorrow (separate categories)
|
||
8. **Unbooked travel** — flights that need booking. Flag deadlines.
|
||
9. **Sleep conflicts** — appointments in next 48h that fall within predicted
|
||
sleep window
|
||
10. **Sleep** — one-line summary of last sleep + drift trend + concerns
|
||
11. **Unanswered questions** — questions the agent asked that haven't been
|
||
answered
|
||
12. **Weather alerts** — only if significant or extreme. Use METAR from
|
||
aviationweather.gov. Omit if nothing notable.
|
||
13. **Overdue reminders** — anything pending
|
||
14. **Open projects** — one-line status per project
|
||
|
||
Before generating a sitrep, review the last 3 days of daily memory files for
|
||
context. **If anything notable isn't covered by the items above — recent
|
||
lessons, pending decisions, things you think the user should know about — add
|
||
additional sections as needed.**
|
||
|
||
Omit any item that is "none" or zero. Keep it scannable. Use bullet points, not
|
||
prose. Only surface things the user might not know about or needs to act on.
|
||
```
|
||
|
||
### Key design decisions:
|
||
|
||
- **"Add additional sections as needed"** — this is the magic line. It lets the
|
||
agent surface things you didn't think to ask about. If there's a pending
|
||
decision from 2 days ago, or a lesson from a recent mistake, it'll include it.
|
||
- **"Omit any item that is none or zero"** — keeps it clean. No "Overdue
|
||
reminders: none" clutter.
|
||
- **Relative times** ("4 days from now, Tuesday") are more useful than bare
|
||
dates
|
||
- **Review last 3 days** ensures the agent has recent context, not just today
|
||
- The sitrep pulls from multiple data sources (daily context, calendars, issue
|
||
trackers, memory files) — it's a dashboard in prose form
|
||
|
||
### Time display tip:
|
||
|
||
```markdown
|
||
Include current time in local timezone, UTC, and any other relevant timezone at
|
||
the top.
|
||
```
|
||
|
||
This anchors the reader and makes all the relative times unambiguous.
|
||
|
||
---
|
||
|
||
## Sleep Tracking
|
||
|
||
The agent infers sleep from message activity gaps, not from explicit "I'm going
|
||
to sleep" statements (though those help too).
|
||
|
||
### File: `memory/sleep-log.csv`
|
||
|
||
```csv
|
||
Date,SleepTime,WakeTime,TimeZone,Duration,Status,Notes
|
||
2026-02-07,08:30,15:30,America/Los_Angeles,7h00m,actual,Approximate times
|
||
2026-02-08,~12:00,~15:30,America/Los_Angeles,~3h30m,actual,Short sleep
|
||
```
|
||
|
||
### AGENTS.md prompt:
|
||
|
||
```markdown
|
||
### Sleep Tracking
|
||
|
||
- **`memory/sleep-log.csv`** — columns: Date, SleepTime, WakeTime, TimeZone,
|
||
Duration, Status, Notes
|
||
- Infer sleep/wake times from message activity and explicit statements
|
||
- User's sleep pattern drifts later each day; typical duration 4–8 hours
|
||
- Update daily-context.json isSleeping field accordingly
|
||
- **On every message from user:** if there was a communication gap overlapping
|
||
predicted sleep time, infer a sleep window (sleep start = last activity before
|
||
gap, wake = first activity after gap). Activity includes direct messages, git
|
||
commits/comments, scheduled flight departures, and any other observable
|
||
actions — not just chat messages. Log based on observed gaps only, never from
|
||
mathematical predictions.
|
||
```
|
||
|
||
### Daily context fields for sleep:
|
||
|
||
```json
|
||
{
|
||
"isSleeping": false,
|
||
"lastKnownWakeTime": "2026-02-28T12:30:00+07:00",
|
||
"predictedSleepTime": "2026-03-01T05:30:00+07:00",
|
||
"predictedWakeTime": "2026-03-01T12:45:00+07:00",
|
||
"lastSleep": "actual: 2026-02-28 05:15-12:30 ICT (~7h15m). Stable pattern."
|
||
}
|
||
```
|
||
|
||
### How prediction works:
|
||
|
||
The agent observes the pattern drift (e.g., sleeping 30min later each day) and
|
||
extrapolates. Predictions are updated after each actual observation. The agent
|
||
uses predictions to:
|
||
|
||
- Avoid sending non-urgent alerts during predicted sleep
|
||
- Flag sleep conflicts with upcoming appointments in sitreps
|
||
- Factor caffeine intake into predictions (caffeine = 5-6h minimum awake)
|
||
|
||
### Caffeine integration:
|
||
|
||
```json
|
||
{
|
||
"lastCaffeineTimestamp": "2026-02-10T00:45:00-08:00"
|
||
}
|
||
```
|
||
|
||
Track caffeine in daily-context.json. If the user has caffeine, adjust sleep
|
||
prediction forward by their typical caffeine-to-sleep duration.
|
||
|
||
---
|
||
|
||
## Location & Timezone Tracking
|
||
|
||
The agent always knows where you are and adjusts all time displays accordingly.
|
||
|
||
### File: `memory/location-log.csv`
|
||
|
||
```csv
|
||
Date,City,Country,Timezone
|
||
2026-02-08,Las Vegas,US,America/Los_Angeles
|
||
2026-02-11,Berlin,DE,Europe/Berlin
|
||
2026-02-18,Bangkok,TH,Asia/Bangkok
|
||
```
|
||
|
||
### Timezone rules (AGENTS.md):
|
||
|
||
```markdown
|
||
## Timezone/Time Rules
|
||
|
||
- System clock is PST. User's TZ is in daily-context.json — always convert.
|
||
- "Today"/"tomorrow" = user's local TZ, not system clock.
|
||
- Never state times without explicit conversion.
|
||
- When logging medication times, always use the user's current timezone.
|
||
```
|
||
|
||
### Why this matters:
|
||
|
||
OpenClaw runs on a server (probably in a fixed timezone). Your human travels.
|
||
Without explicit timezone tracking:
|
||
|
||
- "Take your meds" fires at 3am local time
|
||
- "Today" means the wrong day
|
||
- Sleep predictions are off by hours
|
||
- Calendar events show wrong times
|
||
|
||
The daily-context.json `currentTimezone` field is the single source of truth.
|
||
Everything else derives from it.
|
||
|
||
---
|
||
|
||
## Medication Tracking
|
||
|
||
This is a safety-critical system. The design prioritizes never missing a dose
|
||
and never double-dosing over convenience.
|
||
|
||
### Files:
|
||
|
||
- `memory/medications-instructions.md` — the authoritative medication list,
|
||
dosages, schedules, and safety rules
|
||
- `memory/medications-log-YYYY-MM.csv` — one row per ingestion, split by month
|
||
- `memory/checklist-medications.md` — pre-action verification checklist
|
||
|
||
### Log format: `memory/medications-log-YYYY-MM.csv`
|
||
|
||
```csv
|
||
Date,Time,TimeZone,Medication,Dosage
|
||
2026-02-27,11:43,Asia/Bangkok,Metoprolol,50 mg
|
||
2026-02-27,11:43,Asia/Bangkok,Omeprazole,40 mg
|
||
2026-02-27,11:43,Asia/Bangkok,Aspirin,162 mg
|
||
```
|
||
|
||
### Instructions file structure (`memory/medications-instructions.md`):
|
||
|
||
```markdown
|
||
# Medication Instructions
|
||
|
||
## General Rules
|
||
|
||
- Maintain cumulative CSV logs split by month as the authoritative record
|
||
- CSV columns (fixed order): Date,Time,TimeZone,Medication,Dosage
|
||
- "Today"/"yesterday"/"tomorrow" always mean relative to currentTimezone in
|
||
daily-context.json — NEVER relative to system clock
|
||
- Always spell medication names correctly; silently correct transcription errors
|
||
|
||
## Daily Medications
|
||
|
||
One batch per calendar day unless explicitly confirmed otherwise. "Log my daily
|
||
meds" = log each as a separate row.
|
||
|
||
**CRITICAL: Never trust hasTakenDailyMedsToday without verifying
|
||
dailyMedsTimestamp.**
|
||
|
||
- Resolve the current date in user's currentTimezone FIRST
|
||
- Check if dailyMedsTimestamp falls on THAT date in THAT timezone
|
||
- If the timestamp is from a previous calendar day, meds are NOT taken today
|
||
regardless of the boolean
|
||
|
||
**Ideal dosing window:** every 24h ±4h. Hard minimum: 14h between doses.
|
||
|
||
| Medication | Dosage |
|
||
| ----------- | ------ |
|
||
| (your meds) | (dose) |
|
||
|
||
## Interval-Based Medications
|
||
|
||
Scheduling anchored to last actual dose, NOT intended dates.
|
||
|
||
| Medication | Dosage | Interval |
|
||
| ---------- | ------ | ------------ |
|
||
| (med name) | (dose) | Every X days |
|
||
|
||
**Missed dose rules:**
|
||
|
||
- If missed, next dose date is unknown until the missed dose is taken and logged
|
||
- Interval restarts from actual ingestion timestamp
|
||
- Missed doses block future scheduling
|
||
|
||
## Safety Rules (HIGHEST PRIORITY)
|
||
|
||
1. Always triple-check before instructing any medication:
|
||
- Last actual dose taken
|
||
- Required dosing interval
|
||
- Current eligibility without overdose risk
|
||
2. If any ambiguity exists, **do not instruct dosing**
|
||
3. Immediately block logging if same-day duplicate daily-med batch or other
|
||
overdose pattern appears
|
||
4. Require explicit confirmation in any overdose-risk scenario
|
||
```
|
||
|
||
### Verification checklist (`memory/checklist-medications.md`):
|
||
|
||
```markdown
|
||
# Medications Checklist
|
||
|
||
## Before reporting medication status
|
||
|
||
1. Check medications-log-YYYY-MM.csv for actual entries, not dailyMedsTimestamp
|
||
2. Verify dailyMedsTimestamp is from today in user's timezone (not system clock)
|
||
3. Cross-reference medications-instructions.md for what's due
|
||
|
||
## Before sending medication reminders
|
||
|
||
1. Confirm current time in user's timezone
|
||
2. Check if already taken today (verify against CSV, not boolean)
|
||
3. For interval meds: calculate days since last dose from CSV
|
||
4. Never send PII (medication names) via public notification channels
|
||
```
|
||
|
||
### Overdue escalation:
|
||
|
||
```markdown
|
||
**CRITICAL: If daily meds are >26h since last dose, escalate aggressively.** Use
|
||
urgent notification channel if chat messages go unacknowledged. Do not let this
|
||
slide silently. Always include hours since last dose in reminders (e.g. "daily
|
||
meds overdue — last dose was 27h ago").
|
||
```
|
||
|
||
### Why the double-check on the boolean:
|
||
|
||
The daily-context.json has a `hasTakenDailyMedsToday` boolean AND a
|
||
`dailyMedsTimestamp`. A midnight-reset cron flips the boolean to false. But if
|
||
the user is in a timezone ahead of the server, the boolean may reset before
|
||
their actual "today" ends — or after it began. The rule: **always verify the
|
||
timestamp falls on today's date in the user's timezone.** The boolean is a
|
||
convenience hint, not the source of truth.
|
||
|
||
---
|
||
|
||
## Flight & Travel Logging
|
||
|
||
### Files:
|
||
|
||
- `memory/flight-log.csv` — one row per flight segment
|
||
- `memory/location-log.csv` — one row per calendar day
|
||
- `memory/travel.md` — upcoming travel plans, booking status
|
||
- `memory/checklist-flights.md` — pre-action checklist
|
||
|
||
### Flight log format:
|
||
|
||
```csv
|
||
Date,FlightNumber,Origin,Destination,Duration,Alliance
|
||
2026-02-10,DL9670,LAS,AMS,10h10m,SkyTeam
|
||
2026-02-10,KL1856,AMS,BER,1h25m,SkyTeam
|
||
```
|
||
|
||
### Flight prep blocks (put in HEARTBEAT.md):
|
||
|
||
```markdown
|
||
## Flight Prep Blocks (daily)
|
||
|
||
Run `khal list today 7d`. For flights missing "shower and dress" / "travel to
|
||
airport" blocks, create them:
|
||
|
||
- shower_start = flight_departure - airport_buffer - travel_buffer - 1h
|
||
- Airport buffer: 2h domestic, 2.5h international
|
||
- Travel to airport: 1h default
|
||
- Create "shower and dress" block at shower_start (1h duration)
|
||
- Create "travel to airport" block after shower
|
||
- Set cron reminders: 15min before shower start, at departure time
|
||
- Skip layover connections (already in airport)
|
||
```
|
||
|
||
This means the agent automatically works backwards from flight times to create
|
||
preparation blocks in the calendar. No manual planning needed.
|
||
|
||
---
|
||
|
||
## Landing Checklist
|
||
|
||
Triggered automatically after every flight lands. The agent doesn't wait to be
|
||
asked.
|
||
|
||
### File: `memory/landing-checklist.md`
|
||
|
||
```markdown
|
||
# Landing Checklist
|
||
|
||
Run this after EVERY flight, regardless of whether location/timezone changes.
|
||
|
||
## Immediate (within minutes of landing)
|
||
|
||
- [ ] Update daily-context.json → currentLocation, currentLodging,
|
||
currentTimezone, nearestAirport, travelLeg
|
||
- [ ] Update midnight-reset cron job timezone to new currentTimezone
|
||
- [ ] Update location-log.csv with new city for today
|
||
- [ ] Log flight segment in flight-log.csv
|
||
|
||
## Within 1 hour
|
||
|
||
- [ ] Check if daily meds are due/overdue (timezone change may shift the window)
|
||
- [ ] Check if any interval meds are due today
|
||
- [ ] Sync calendar and review next 48h for the new timezone
|
||
- [ ] Check for any cron jobs with hardcoded timezones that need updating
|
||
|
||
## If entering a new country
|
||
|
||
- [ ] Check for medication resupply needs
|
||
- [ ] Note any upcoming reminders that reference the old timezone
|
||
```
|
||
|
||
### AGENTS.md prompt:
|
||
|
||
```markdown
|
||
### Landing Checklist
|
||
|
||
- After EVERY flight the user takes, run through `memory/landing-checklist.md`
|
||
- Trigger: calendar event landing time, or user messages after a flight
|
||
- Do not wait to be asked — run it proactively
|
||
```
|
||
|
||
---
|
||
|
||
## Memory Architecture
|
||
|
||
The two-tier memory system: daily files (raw) + MEMORY.md (curated).
|
||
|
||
### Daily files: `memory/YYYY-MM-DD.md`
|
||
|
||
Raw logs of what happened each day. Topics discussed, decisions made, tasks
|
||
completed. Every session can read these.
|
||
|
||
```markdown
|
||
# 2026-02-28
|
||
|
||
## Topics
|
||
|
||
| Time | Topic | Session |
|
||
| ----- | ---------------------------- | ------- |
|
||
| 14:30 | Discussed PR review workflow | main |
|
||
| 16:00 | Medication logged | main |
|
||
|
||
## Notes
|
||
|
||
- Decided to switch deployment strategy for project X
|
||
- User prefers Y approach over Z — standing rule
|
||
```
|
||
|
||
### MEMORY.md — Long-term curated memory
|
||
|
||
```markdown
|
||
## 🧠 MEMORY.md - Your Long-Term Memory
|
||
|
||
- **ONLY load in main session** (direct chats with your human)
|
||
- **DO NOT load in shared contexts** (group chats, sessions with others)
|
||
- This is for **security** — contains personal context that shouldn't leak
|
||
- Write significant events, thoughts, decisions, opinions, lessons learned
|
||
- This is your curated memory — the distilled essence, not raw logs
|
||
- Over time, review daily files and update MEMORY.md with what's worth keeping
|
||
```
|
||
|
||
### Memory maintenance (in AGENTS.md or HEARTBEAT.md):
|
||
|
||
```markdown
|
||
### 🔄 Memory Maintenance (During Heartbeats)
|
||
|
||
Periodically (every few days), use a heartbeat to:
|
||
|
||
1. Read through recent memory/YYYY-MM-DD.md files
|
||
2. Identify significant events, lessons, or insights worth keeping long-term
|
||
3. Update MEMORY.md with distilled learnings
|
||
4. Remove outdated info from MEMORY.md that's no longer relevant
|
||
|
||
Think of it like reviewing a journal and updating a mental model. Daily files
|
||
are raw notes; MEMORY.md is curated wisdom.
|
||
```
|
||
|
||
### Critical rule — write it down:
|
||
|
||
```markdown
|
||
### 📝 Write It Down - No "Mental Notes"!
|
||
|
||
- **Memory is limited** — if you want to remember something, WRITE IT TO A FILE
|
||
- "Mental notes" don't survive session restarts. Files do.
|
||
- When someone says "remember this" → update memory file
|
||
- When you learn a lesson → update AGENTS.md or relevant file
|
||
- When you make a mistake → document it so future-you doesn't repeat it
|
||
- **Text > Brain** 📝
|
||
```
|
||
|
||
---
|
||
|
||
## Heartbeat Configuration
|
||
|
||
Heartbeats are periodic polls from OpenClaw. The agent can do background work or
|
||
stay quiet.
|
||
|
||
### HEARTBEAT.md structure:
|
||
|
||
```markdown
|
||
# HEARTBEAT.md
|
||
|
||
## Inbox Check (PRIORITY)
|
||
|
||
(check notifications, issues, emails — whatever applies)
|
||
|
||
## Flight Prep Blocks (daily)
|
||
|
||
(create calendar prep blocks for upcoming flights)
|
||
|
||
## Open Projects Review
|
||
|
||
(check project status, take next steps, or ask if blocked)
|
||
|
||
## Workspace Sync
|
||
|
||
(commit and push workspace changes)
|
||
|
||
## Rules
|
||
|
||
- Post status updates to a designated channel, not the main chat
|
||
- Update state files after any state change
|
||
- If nothing needs attention, reply HEARTBEAT_OK
|
||
|
||
## Output Rules
|
||
|
||
Never send internal thinking or status narration to user's DM. Output should be:
|
||
|
||
- HEARTBEAT_OK (if nothing needs attention)
|
||
- A direct question or alert (if action needed)
|
||
- Work narration → send to a status channel via message tool
|
||
```
|
||
|
||
### Tracking heartbeat state: `memory/heartbeat-state.json`
|
||
|
||
```json
|
||
{
|
||
"lastChecks": {
|
||
"email": 1703275200,
|
||
"calendar": 1703260800,
|
||
"weather": null,
|
||
"gitea": 1703280000
|
||
},
|
||
"lastWeeklyDocsReview": "2026-02-24"
|
||
}
|
||
```
|
||
|
||
### Heartbeat vs Cron:
|
||
|
||
```markdown
|
||
**Use heartbeat when:**
|
||
|
||
- Multiple checks can batch together
|
||
- You need conversational context from recent messages
|
||
- Timing can drift slightly (every ~30 min is fine)
|
||
- You want to reduce API calls by combining periodic checks
|
||
|
||
**Use cron when:**
|
||
|
||
- Exact timing matters ("9:00 AM sharp every Monday")
|
||
- Task needs isolation from main session history
|
||
- You want a different model or thinking level for the task
|
||
- One-shot reminders ("remind me in 20 minutes")
|
||
```
|
||
|
||
---
|
||
|
||
## Gitea Integration & Notification Polling
|
||
|
||
For self-hosted Gitea instances, you can set up a notification poller that
|
||
injects Gitea events (issue assignments, PR reviews, @-mentions) into the
|
||
agent's session.
|
||
|
||
### Workflow rules (HEARTBEAT.md / AGENTS.md):
|
||
|
||
```markdown
|
||
## Gitea Work Scope
|
||
|
||
Find issues/PRs assigned to me or where I'm @-mentioned → do the work.
|
||
|
||
- If @-mentioned with work but not assigned → assign myself, remove other
|
||
assignees
|
||
- When nothing is assigned to me, my Gitea work is done
|
||
- PRs assigned to user = their queue, not my backlog
|
||
|
||
Workflow: issue → branch → PR "(closes #X)" → review/rework → assign user when
|
||
all checks pass + reviewed.
|
||
|
||
### Rules
|
||
|
||
- Respond in the medium addressed: Gitea → Gitea comment, not chat
|
||
- Before acting on ANY issue or PR: read ALL existing comments first
|
||
- Never do work on a PR/issue that isn't assigned to you
|
||
- Work done, no more needed → close the issue
|
||
- Your part done, need feedback → unassign yourself, assign next person
|
||
```
|
||
|
||
### Notification poller:
|
||
|
||
A Python script that polls the Gitea notifications API and injects events into
|
||
OpenClaw sessions. It runs via launchd/systemd. Ask me for the full source code
|
||
if you want to set this up — I can share it (it's a general-purpose tool with no
|
||
PII).
|
||
|
||
---
|
||
|
||
## Sub-Agent Management
|
||
|
||
For complex coding tasks, spawn isolated sub-agents.
|
||
|
||
### Key rules:
|
||
|
||
```markdown
|
||
### Sub-Agent Git Isolation (MANDATORY)
|
||
|
||
- NEVER let multiple sub-agents share the same git clone
|
||
- Each sub-agent MUST clone to a unique temporary directory (e.g. `mktemp -d`)
|
||
- When spawning, always instruct: `cd $(mktemp -d) && git clone <url> . && ...`
|
||
- Dirty working directories cause false CI results
|
||
|
||
### Sub-Agent PR Quality Gate (MANDATORY)
|
||
|
||
- `make check` must pass with ZERO failures. No exceptions.
|
||
- Pre-existing failures are YOUR problem. Fix them as part of your PR.
|
||
- NEVER modify linter config to make checks pass. Fix the code.
|
||
- Every PR must include full `make check` output
|
||
- Rebase before and after committing
|
||
- Never self-review
|
||
```
|
||
|
||
---
|
||
|
||
## Urgent Notifications via ntfy
|
||
|
||
For time-sensitive alerts when chat messages might go unacknowledged (e.g.,
|
||
overdue medications):
|
||
|
||
```markdown
|
||
## Urgent Notifications
|
||
|
||
Send urgent messages via ntfy.sh:
|
||
|
||
curl -d "MESSAGE HERE" ntfy.sh/YOUR-PRIVATE-TOPIC-ID
|
||
|
||
Use this for time-sensitive alerts (overdue meds, critical issues, etc.).
|
||
**Never send PII via ntfy.** Keep messages generic (e.g. "daily meds overdue"
|
||
not medication names or personal details).
|
||
```
|
||
|
||
Create a random topic ID for your ntfy channel. The agent can `curl` to it
|
||
directly.
|
||
|
||
---
|
||
|
||
## Group Chat Behavior
|
||
|
||
Rules for when the agent is in group chats with multiple people:
|
||
|
||
```markdown
|
||
### Know When to Speak
|
||
|
||
**Respond when:**
|
||
|
||
- Directly mentioned or asked a question
|
||
- You can add genuine value (info, insight, help)
|
||
- Something witty/funny fits naturally
|
||
- Correcting important misinformation
|
||
|
||
**Stay silent when:**
|
||
|
||
- Just casual banter between humans
|
||
- Someone already answered the question
|
||
- Your response would just be "yeah" or "nice"
|
||
- The conversation is flowing fine without you
|
||
|
||
**The human rule:** Humans don't respond to every message. Neither should you.
|
||
Quality > quantity.
|
||
|
||
### React Like a Human
|
||
|
||
Use emoji reactions naturally:
|
||
|
||
- Appreciate something but don't need to reply → 👍, ❤️
|
||
- Something made you laugh → 😂
|
||
- Interesting/thought-provoking → 🤔, 💡
|
||
- Acknowledge without interrupting → ✅, 👀
|
||
|
||
One reaction per message max.
|
||
```
|
||
|
||
---
|
||
|
||
## Requirement Capture
|
||
|
||
Never lose a rule or preference your human states:
|
||
|
||
```markdown
|
||
### Requirement Capture (MANDATORY)
|
||
|
||
- **Every single requirement, rule, preference, or instruction the user provides
|
||
MUST be captured in the daily memory file immediately**
|
||
- This includes: project rules, workflow preferences, corrections, new policies,
|
||
technical decisions
|
||
- If the user says something should be done a certain way, write it down in
|
||
memory/YYYY-MM-DD.md AND in MEMORY.md if it's a standing rule
|
||
- Nothing is to be missed. If in doubt, log it.
|
||
```
|
||
|
||
---
|
||
|
||
## PII Output Routing — Audience-Aware Responses
|
||
|
||
A critical security pattern: **the audience determines what you can say, not who
|
||
asked.** If your human asks for a sitrep (or any PII-containing info) in a group
|
||
channel, you can't just dump it there — other people can read it.
|
||
|
||
### AGENTS.md / checklist prompt:
|
||
|
||
```markdown
|
||
## PII Output Routing (CRITICAL)
|
||
|
||
- NEVER output PII in any non-private channel, even if your human asks for it
|
||
- If a request would produce PII (medication status, travel details, financial
|
||
info, etc.) in a shared channel: send the response via DM instead, and reply
|
||
in-channel with "sent privately"
|
||
- The rule is: the audience determines what you can say, not who asked
|
||
- This applies to: group chats, public issue trackers, shared Mattermost
|
||
channels, Discord servers — anywhere that isn't a 1:1 DM
|
||
```
|
||
|
||
### Why this matters:
|
||
|
||
This is a real failure mode. If someone asks "sitrep" in a group channel and you
|
||
respond with medication names, partner details, travel dates, and hotel names —
|
||
you just leaked all of that to everyone in the channel. The human asking is
|
||
authorized to see it; the channel audience is not. Always check WHERE you're
|
||
responding, not just WHO asked.
|
||
|
||
---
|
||
|
||
## General Tips
|
||
|
||
### Bias toward action
|
||
|
||
Don't ask blocking questions with obvious answers. Figure it out. Come back with
|
||
results, not requests for permission.
|
||
|
||
### Checklists over trust
|
||
|
||
For safety-critical operations (meds, deployments, external communications),
|
||
maintain checklists in `memory/checklist-*.md` and reference them from
|
||
AGENTS.md. The agent reads the checklist before acting.
|
||
|
||
### State repo
|
||
|
||
Keep your workspace in a git repo that auto-syncs. This gives you version
|
||
history and recovery:
|
||
|
||
```markdown
|
||
### State Repo
|
||
|
||
- Commit and push workspace changes after any change
|
||
- Push promptly — remote should reflect workspace in near-realtime
|
||
- Auto-sync cron runs every 6h as a safety net
|
||
```
|
||
|
||
### Error handling philosophy
|
||
|
||
```markdown
|
||
## On Errors
|
||
|
||
When something goes wrong:
|
||
|
||
1. Identify what input caused the wrong output
|
||
2. Fix the input (rule, checklist, prompt, automation)
|
||
3. Move on
|
||
|
||
No apologies. No promises. Fix the system.
|
||
```
|
||
|
||
### Never weaken checks
|
||
|
||
```markdown
|
||
## On Integrity
|
||
|
||
Never cheat. When a check fails, fix the code — don't weaken the check. When a
|
||
test fails, fix the bug — don't loosen the assertion. When a linter flags
|
||
something, fix the finding — don't suppress the rule.
|
||
```
|
||
|
||
---
|
||
|
||
_This document is a living reference. Patterns here have been tested in
|
||
production and refined through real-world use._
|