clawpub/OPENCLAW_TRICKS.md

1274 lines
43 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
### 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 48 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._