Autonomous Patterns
REACH in autonomous mode operates without a human in the loop for the routine work. Claude authors the orchestration logic, schedules it via Windows Task Scheduler, and the system wakes, reaches, records, signals, and sleeps — on its own cadence.
Human judgment isn't eliminated. It's reserved for the moments that warrant it.
The Core Autonomous Loop
Wake (Task Scheduler trigger)
↓
Check state — what needs attention?
↓
Reach into sources (Outlook, DB, git, web)
↓
Act (send, record, launch, signal)
↓
Write state — what did I do?
↓
Log — structured, reconstructable
↓
Sleep (process exits, scheduler owns the next wake)Every autonomous reach follows this shape. The discipline is in the state and log layers — without them, unattended runs are a black box.
Scheduling — Windows Task Scheduler
No daemon. No always-on process. Task Scheduler wakes the reach, it runs, it exits. Energy efficient, no memory leak risk, no zombie processes.
$action = New-ScheduledTaskAction -Execute "dotnet" `
-Argument "run C:\reach\outlook-check.cs" `
-WorkingDirectory "C:\reach"
$trigger = New-ScheduledTaskTrigger -Daily -At "8:00AM"
Register-ScheduledTask -TaskName "REACH-OutlookCheck" `
-Action $action -Trigger $trigger -RunLevel HighestAvailable triggers:
- Daily / weekly at fixed time
- On system startup
- On user login
- On file system event (via wrapper)
- On network availability
- Chained — one task signals the next
State Management
Each reach starts fresh. Autonomous runs need explicit state or they repeat work, miss things, or lose track of where they left off.
var statePath = @"C:\reach\state\outlook-check.json";
var state = File.Exists(statePath)
? JsonSerializer.Deserialize<CheckState>(File.ReadAllText(statePath))
: new CheckState { LastProcessedId = null, LastRunAt = DateTime.MinValue };
// ... do work using state.LastProcessedId to skip already-seen items ...
// Write updated state
File.WriteAllText(statePath, JsonSerializer.Serialize(new CheckState
{
LastProcessedId = latestId,
LastRunAt = DateTime.UtcNow
}));State files are flat JSON. Human readable. Git trackable. The reach owns them — nothing else writes to them.
Signal Files — Inter-Reach Communication
Signal files coordinate independent reaches without a message broker. One reach writes a signal; another reads it and acts.
C:\reach\signals\
urgent.signal ← outlook-check wrote this
build-failed.signal ← ci-monitor wrote this
file-arrived.signal ← file-watcher wrote thisWriting a signal:
reach process.signal "urgent"
payload subject from "email"
output logReading and consuming a signal:
reach file.watch "C:\reach\signals"
on new signal "urgent"
then prompt.confirm
title "Urgent item flagged — review now?"
on yes reach outlook.inbox where flagged trueFailure Handling
Autonomous reaches fail silently by default — no one is watching. The log layer is the safety net.
Dead letter pattern — failed items written to a dead-letter folder for manual review:
C:\reach\dead-letter\
2026-06-17-0847-timesheet-failed.json
2026-06-16-1200-signal-unhandled.jsonRetry pattern — state file tracks retry count:
if (state.RetryCount >= 3)
{
// Write to dead-letter, skip this item
File.WriteAllText($@"C:\reach\dead-letter\{itemId}.json",
JsonSerializer.Serialize(new { item, error, state.RetryCount }));
}Anomaly detection — OCTO Health Layer reads run history and surfaces patterns: arm failed 2+ consecutive runs, process didn't wake at scheduled time, empty result where data was expected. See Health Layer →.
Autonomous OCTO — Morning Brief
The morning brief is OCTO running autonomously at 8:45am. No human initiates it. The Task Scheduler wakes it, the arms reach, the surface appears on the screen, and it waits for the human.
# morning.octo
name morning-brief
arms
- outlook.inbox since yesterday analyze urgent
- teams.planner status overdue
- teams.transcript latest summarize
- git.commits since yesterday analyze cadence
surface
title "Good morning. Here's what needs you."
show urgent-items
actions
draft-reply → reach outlook.draft from selected
send-summary → reach outlook.draft to team summarize all
snooze → reach teams.planner snooze selected
close
tone warm
joke trueScheduled via Task Scheduler. Runs daily at 8:45. Arms reach, surface appears, human decides, OCTO closes. Process exits until tomorrow.
Multi-Reach Coordination
Multiple autonomous reaches coordinating through signals and state:
8:00 outlook-check.reach → reads inbox, writes urgent.signal if needed
8:45 morning.octo → reads combined signal, surfaces brief
12:00 midday-check.reach → reads schedule changes, cancellations
17:00 timesheet-draft.reach → reconstructs day from git + calendar
17:00 daily-report.octo → reads run history, surfaces daily reportEach reach is independent. They coordinate through the signals folder and shared state files. No message queue. No broker. No infrastructure.
Design Principles for Autonomous REACH
State is explicit — the reach owns its state file. Nothing else writes to it. If the state file is missing, start fresh — don't guess.
Log everything you do — autonomous runs have no observer. The log is the only way to reconstruct what happened.
Dead-letter failed items — never silently discard failures. Write them somewhere a human can find and review them.
Signals, not direct calls — reaches coordinate through signal files, not direct invocation. Loose coupling. A signal can be consumed by whichever reach picks it up next.
Bounded token consumption — each autonomous run consumes tokens for its task and exits. No continuous burn. The cost is per run, not per hour.
The human is still there — autonomous doesn't mean unattended forever. The morning brief surfaces every day. The daily report closes every evening. The human is in the loop at the cadence the practice is designed for — not at every step, but at the right moments.