| name | three-shifts |
| description | Cyclic shift execution engine. Plans tasks 3x daily (6 AM, 2 PM, 10 PM), decomposes them into granular steps, then executes via 15-minute cron cycles. Each cycle reads state files, picks the next step, executes it, writes results back. Errors are logged and skipped — never fatal. Planning uses Claude 4.6; execution uses GLM-5.
|
| version | 2.0.0 |
Three Shifts v2 — Cyclic Execution Engine
Architecture: Plan once → decompose → execute in 15-minute cycles → each cycle does ONE step → state persists in files between runs.
Why v2: V1 ran everything in one long session. One timeout killed the whole shift. V2 is a state machine — errors get logged, skipped, and retried. 32 cycles per shift, each learning from the last.
File Structure
All state lives in workspace/shifts/:
shifts/
├── tasks.md # Current shift's step-by-step plan (the state machine)
├── context.md # Stack, constraints, known pitfalls, learned lessons
├── handoff.md # What the previous shift left behind
├── state.json # Machine-readable shift metadata
└── history/ # Archived completed shifts
└── YYYY-MM-DD-{shift}.md
tasks.md Format
# Afternoon Shift — 2026-02-22
Approved by: user | Approved at: 2:15 PM CST
Shift window: 2:00 PM – 10:00 PM CST
Cycles remaining: ~30
## Task 1: API migration plan [P1]
- [x] Step 1.1: Read current proxy-router config and session economics — DONE (cycle 1)
- [ ] Step 1.2: Research mor-[REDACTED] paid tier pricing and limits
- [!] Step 1.3: Compare cost of paid tier vs local-only — BLOCKED: need pricing from step 1.2
- [ ] Step 1.4: Draft transition recommendation in memory/projects/example/
## Task 2: Commit workspace changes [P1]
- [ ] Step 2.1: Run git status, identify logical commit groups
- [ ] Step 2.2: Stage and commit new skills (three-shifts, relationships)
- [ ] Step 2.3: Stage and commit memory reorganization files
- [ ] Step 2.4: Stage and commit scripts and config changes
- [ ] Step 2.5: Push to remote
## Summary
Total steps: 9 | Done: 1 | Blocked: 1 | Remaining: 7
Last cycle: 2026-02-22 14:15 CST — completed step 1.1
Next target: Step 1.2
Step markers:
[ ] — Not started (available for next cycle)
[x] — Completed (with cycle number and brief result)
[!] — Blocked (with reason — will be retried after dependencies clear)
[~] — In progress (claimed by current cycle, prevents double-execution)
[-] — Skipped (manually removed or deprioritized)
context.md Format
# Shift Context — Living Document
## Stack & Environment
- macOS arm64, OpenClaw 2026.2.21-2
- Git repos: ~/.openclaw/workspace
- 1Password for secrets (never store raw keys)
- Primary messaging channel for user comms
## Constraints
- Never force push or delete branches
- trash > rm (recoverable beats gone)
- Space Brave searches by 2+ seconds (rate limit)
- MiniMax-M2.5: DO NOT USE (broken streaming)
- Llama 3.3 70B: DO NOT USE
## Known Pitfalls (learned from experience)
- API-specific gotchas go here
- Check messaging connectivity if deliveries fail
- Check provider billing cooldown policies
- Always check git status before committing — avoid partial commits
## Rules for This Shift
- Night shift: no external comms, no financial txns, no destructive ops
- If blocked on user input, skip and note in handoff
- Max 1 web search per cycle (Brave rate limit)
state.json Format
{
"shift": "afternoon",
"date": "2026-02-22",
"status": "executing",
"startedAt": "2026-02-22T20:00:00Z",
"approvedAt": "2026-02-22T20:15:00Z",
"approvedBy": "user",
"planModel": "your-heavy-model (e.g. Claude, GPT-4)",
"execModel": "your-light-model (e.g. GLM-5, GPT-4o-mini)",
"totalSteps": 9,
"completed": 1,
"blocked": 1,
"skipped": 0,
"cyclesRun": 1,
"lastCycleAt": "2026-02-22T20:15:00Z",
"nightAutoApproved": false,
"carryoverFromShift": null
}
Shifts
| Shift | Plan Cron | Cycle Crons | Window | Character |
|---|
| Morning | 6:00 AM | 6:15 AM – 1:45 PM (every 15 min) | 6 AM – 2 PM | Comms, decisions, user-facing |
| Afternoon | 2:00 PM | 2:15 PM – 9:45 PM (every 15 min) | 2 PM – 10 PM | Deep work, building, coding |
| Night | 10:00 PM | 10:15 PM – 5:45 AM (every 15 min) | 10 PM – 6 AM | Autonomous, research, maintenance |
Phase 1: Planning (runs once per shift)
Model: your-heavy-model (e.g. Claude, GPT-4) (heavy model for quality planning)
When the planning cron fires:
1. Gather Context
Read these sources:
shifts/handoff.md — what last shift left behind
shifts/context.md — rules, constraints, pitfalls
shifts/state.json — previous shift stats
memory/daily/YYYY-MM-DD.md — today's activity
MEMORY.md — active projects, priorities, deadlines
- Calendar (if available) — meetings in shift window
- Email (if available) — urgent unreads
- Git status of active repos
2. Check for Carryover
If shifts/tasks.md has incomplete [ ] or [!] steps from a previous shift:
- Carry them forward into the new plan automatically
- Mark them as
[carryover] in the new plan
- Night shift: if ALL remaining items were approved in morning/afternoon, auto-approve (no user ping needed)
3. Generate Shift Plan
Present to user in this format:
☀️/🌤️/🌙 [SHIFT] SHIFT PLAN
Date: [DATE] | Window: [START]–[END] [TZ]
Exec model: GLM-5 | Cycles: ~32
P1 (Must do):
1. [Task] — [Est. time] — [Why now]
2. ...
P2 (Should do):
3. ...
P3 (Could do):
5. ...
Blocked/Waiting:
- [Item] — [Blocker]
Carryover from last shift:
- [Item] — [Status]
Reply: "Approve" / "Approve all" / "Approve 1,3" / "Add: [task]" / "Skip"
4. On Approval — Auto-Decompose
This is the critical v2 step. After approval, before any execution begins:
- Take each approved task and break it into atomic steps that GLM-5 can execute in a single cycle (~10-12 minutes of work max per step)
- Each step must be:
- Self-contained: Can be done without remembering previous cycles
- Verifiable: Has a clear "done" condition
- Small: One file edit, one command, one search, one commit — not five things
- Context-light: The step description + context.md should be enough; no conversation history needed
- Write the decomposed plan to
shifts/tasks.md
- Initialize
shifts/state.json
- Start the cycle cron (or let the existing every-15-min cron pick it up)
Decomposition guidelines for GLM-5:
- Prefer explicit commands over abstract instructions ("Run
git status in your project dir" not "check the repo")
- Include file paths, command snippets, expected outputs where possible
- If a step requires a decision, split it: "Research X" → "Write recommendation to file" → separate step for user to review
- Max 3 tool calls per step (keeps GLM-5 focused)
- If a task has >8 steps, split it into two tasks
5. Night Shift Auto-Approve
If it's 10 PM and:
shifts/tasks.md has incomplete steps from today's morning or afternoon shifts
- Those steps were already approved by the user earlier today
- No new tasks need approval
Then:
- Auto-approve the carryover items
- Skip the user ping
- Write
"nightAutoApproved": true to state.json
- Begin cycling immediately
- Do NOT add new P1/P2 tasks without user approval (P3 autonomous tasks like cleanup/memory are OK)
User Approval Flow
When the user responds to a shift plan:
"Approve" / "Approve all":
- Read
shifts/state.json — confirm status is awaiting_approval
- Set
status to "executing", approvedAt to current ISO timestamp, approvedBy to the user's name`
- Write updated state.json
- The next cycle executor cron (every 15 min) will pick it up automatically
"Add: [task]":
- Decompose the new task into atomic steps (same rules as planning phase)
- Append the new steps to
shifts/tasks.md
- Update
totalSteps in state.json
- Then approve (set status to
"executing")
"Skip":
- Set
status to "cancelled" in state.json
- Write a note in
shifts/handoff.md that the shift was skipped
No response after 30 minutes (morning/afternoon):
- The planning cron already delivered the plan. If no approval comes, cycles will no-op (status stays
awaiting_approval)
- Next shift's planner will carry forward any planned tasks
Phase 2: Cycle Execution (runs every 15 minutes)
Model: your-light-model (e.g. GLM-5, GPT-4o-mini) (light model, cost-efficient)
Each cycle is an isolated session — no memory of previous cycles except what's in the files.
Cycle Algorithm
1. READ shifts/state.json
→ If status is "completed", "cancelled", "idle", or "awaiting_approval": reply HEARTBEAT_OK (no-op)
→ If status is "executing": continue
2. READ shifts/tasks.md
→ Find first step marked [ ] (not started)
→ If none found, check for [!] (blocked) steps — retry if dependency cleared
→ If all steps are [x], [-], or still [!]: shift is DONE → go to Handoff
3. READ shifts/context.md
→ Load rules, constraints, pitfalls
4. CHECK FOR STALE CLAIMS
→ If any step is marked [~] (claimed by a previous cycle that crashed/timed out):
- Change it back to [ ] — the previous cycle didn't finish
- This step becomes the next available step to claim
5. CLAIM the step
→ Mark it [~] (in progress) in tasks.md
→ Write current timestamp to state.json
6. EXECUTE the step
→ Do the work (one focused action)
→ Stay within the step's scope — do NOT drift to other tasks
7. WRITE RESULTS back to tasks.md
→ Success: [x] Step N.N: description — DONE (cycle X): brief result
→ Blocked: [!] Step N.N: description — BLOCKED (cycle X): reason why
→ If blocked, check if next [ ] step has no dependency on this one → continue to it
8. UPDATE state.json
→ Increment cyclesRun, update completed/blocked counts, lastCycleAt
9. UPDATE shifts/context.md (if learned something new)
→ New pitfall discovered? Add it.
→ Found a useful command? Note it.
→ This is how cycles teach future cycles.
10. IF time permits in this cycle, take the NEXT available [ ] step
→ But only if the current step took <5 minutes
→ Never take more than 2 steps per cycle
Cycle Rules
- One step at a time. Don't try to be clever and batch.
- Errors are data, not failures. Log the error, mark blocked, move on.
- Never ask the user mid-cycle. If you need user input, mark blocked and note what's needed.
- Stay in scope. The step says "commit skill files" — don't also refactor the README.
- Update context.md with lessons. This is how you teach the next cycle.
- Respect quiet hours. Night cycles: no messages, no external comms.
- Max 2 steps per cycle. Leave time for the next cycle to start clean.
Handling Blocks
When a step is blocked:
- Mark it
[!] with a clear reason
- Check if the block is:
- Dependency block (step 3 needs step 2's output): Skip, will retry when step 2 clears
- User block (needs user input): Note in handoff, skip
- External block (API down, service unavailable): Retry next cycle automatically
- Error block (command failed): Log error in context.md, retry with different approach next cycle
- Move to the next available
[ ] step
- Blocked steps get re-evaluated each cycle — if the blocker resolves, they become available again
Handling Retries
If a [!] step has been blocked for 3+ cycles:
- Write a note in context.md about the persistent failure
- Check if there's an alternative approach
- If the step has failed 5+ times, mark it
[-] (skipped) and note in handoff for user review
Phase 3: Handoff (automatic when shift ends)
When all steps are [x], [-], or [!], OR when the shift window expires:
-
Archive the shift:
- Copy
shifts/tasks.md → shifts/history/YYYY-MM-DD-{shift}.md
- Include final state.json data as YAML frontmatter
-
Write handoff.md:
# Handoff: Afternoon → Night | 2026-02-22
## Completed
- Task 1: API migration plan — recommendation written to memory/projects/example/
- Task 2 steps 1-3: Workspace commits (skills, memory reorg)
## Still In Progress
- Task 2 steps 4-5: Script commits and push (2 steps remaining)
## Blocked (needs user)
- Task 1 step 3: Need pricing confirmation for mor-[REDACTED] paid tier
## Lessons Learned This Shift
- GLM-5 handles git operations well but struggles with multi-file diffs
- Brave search rate limit hit on cycle 4 — added 3-second delay to context.md
## Recommendations for Next Shift
- Finish Task 2 push (quick, 1 cycle)
- Review migration recommendation in memory/projects/example/transition.md
-
Update state.json: Set status to "completed"
-
Update daily log: Append shift summary to memory/daily/YYYY-MM-DD.md
-
Notify user (morning/afternoon only): Brief summary via messaging channel
- Night shift: write summary but don't ping until morning planning cron
Cron Setup
Planning Crons (3 jobs — Claude 4.6)
Name: three-shifts-plan-morning
Schedule: cron 0 6 * * * (America/Chicago)
Model: your-heavy-model (e.g. Claude, GPT-4)
Session: isolated
Message: >
Read the three-shifts skill at skills/three-shifts/SKILL.md.
Execute Phase 1 (Planning) for the MORNING shift (6 AM – 2 PM CST).
Read shifts/handoff.md, shifts/context.md, shifts/state.json, and recent memory files.
Generate the shift plan and present for user approval.
If carryover items exist from last night that were already approved, note them.
After approval, decompose all tasks into atomic GLM-5-sized steps and write to shifts/tasks.md.
Name: three-shifts-plan-afternoon
Schedule: cron 0 14 * * * (America/Chicago)
Model: your-heavy-model (e.g. Claude, GPT-4)
Session: isolated
Message: >
Read the three-shifts skill at skills/three-shifts/SKILL.md.
Execute Phase 1 (Planning) for the AFTERNOON shift (2 PM – 10 PM CST).
Read shifts/handoff.md, shifts/context.md, shifts/state.json, and recent memory files.
Generate the shift plan and present for user approval.
After approval, decompose all tasks into atomic GLM-5-sized steps and write to shifts/tasks.md.
Name: three-shifts-plan-night
Schedule: cron 0 22 * * * (America/Chicago)
Model: your-heavy-model (e.g. Claude, GPT-4)
Session: isolated
Message: >
Read the three-shifts skill at skills/three-shifts/SKILL.md.
Execute Phase 1 (Planning) for the NIGHT shift (10 PM – 6 AM CST).
Read shifts/handoff.md, shifts/context.md, shifts/state.json, and recent memory files.
IMPORTANT: If shifts/tasks.md has carryover items already approved today, auto-approve them
and set state.json nightAutoApproved=true. Do NOT ping the user for already-approved carryover.
Only present new tasks for approval.
After approval (or auto-approve), decompose into atomic steps and write to shifts/tasks.md.
Execution Crons (1 job — GLM-5, runs every 15 min)
Name: three-shifts-cycle
Schedule: cron */15 * * * * (America/Chicago)
Model: your-light-model (e.g. GLM-5, GPT-4o-mini)
Session: isolated
Message: >
You are a cycle executor for the three-shifts system.
Read skills/three-shifts/SKILL.md Phase 2 (Cycle Execution) for your algorithm.
Then read shifts/state.json — if status is not "executing", reply HEARTBEAT_OK.
If executing: read shifts/tasks.md and shifts/context.md.
Find the first [ ] step, claim it [~], execute it, write result back.
If blocked, mark [!] with reason, move to next [ ] step.
Update state.json and context.md as needed.
Stay focused. One step. Write results. Done.
Configuration
See references/config.md for:
- Customizing shift times
- Weekend behavior
- Quiet hours
- Task categories and safety rules
Safety Rules
Always OK (any shift, any cycle)
- Reading files, git status, web search
- Writing to workspace files (tasks.md, context.md, memory/)
- Running non-destructive commands
Requires Approval (via shift plan, not mid-cycle)
- Sending emails, messages, social posts
- Creating PRs, pushing code
- Financial transactions
Never During Night Shift Cycles
- External communications (messaging, email, social)
- Financial transactions
- Destructive operations (rm, force push, branch delete)
- Security changes (key rotation, permissions)
- New tasks not already approved
Cycle-Specific Safety
- Never take more than 2 steps per cycle
- Never exceed 12 minutes of execution per step
- If a step seems dangerous, mark it
[!] NEEDS_REVIEW instead of executing
- Never modify context.md to remove safety rules
- Never modify this SKILL.md during a cycle