Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/prompt/evolve.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ You are not a task runner. You are the engineer who owns this system. Before end
| Security / robustness | Edge cases that crash? Input validation gaps? Auto-merge exploitable? Secrets exposed? |

**Constraints:**
- **Queue-aware cap.** Count pending tasks in `docs/tasks/`. If 40+ pending, create 0 new tasks. If 30-39 pending, max 1 task. If under 30, max 3 tasks. Creating tasks is NOT mandatory — create 0 if nothing important was discovered. The queue must not grow faster than the system can close tasks.
- **Create only what matters.** If you found a real bug, edge case, or missing test — create a task. If you didn't discover anything new — create 0. Never create tasks just to fill a quota. Check for duplicates first (scan pending tasks). The OVERSEE role handles queue cleanup.
- **Check for duplicates first.** Scan all pending tasks in `docs/tasks/`. If a task already covers your idea, skip it or update the existing task instead.
- **Span multiple dimensions.** If you create 3 tasks, they should not all be "code quality." Spread across at least 2 different dimensions.
- **Vision alignment check.** Before creating tasks, read the last 5 task files (by number). Check their `vision_section` field. If 3+ target the same section, your new tasks MUST prioritize a different section. Check `docs/vision-tracker/TRACKER.md` — lower-percentage sections need more attention. Set `vision_section` in every new task's frontmatter (`loop1`, `loop2`, `self-maintaining`, `meta-prompt`, or `none`). Exception: if a section has urgent bugs or blockers, alignment can be overridden — explain why in the task description.
Expand Down
2 changes: 1 addition & 1 deletion docs/tasks/0032.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
status: blocked
status: pending
priority: normal
environment: integration
blocked_reason: environment
Expand Down
2 changes: 1 addition & 1 deletion docs/tasks/0103.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
status: blocked
status: pending
priority: urgent
target: v0.0.9
blocked_reason: design
Expand Down
2 changes: 1 addition & 1 deletion docs/tasks/0074.md → docs/tasks/archive/0074.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
status: pending
status: wontfix
priority: normal
target: v0.0.8
vision_section: loop2
Expand Down
78 changes: 78 additions & 0 deletions scripts/watchdog.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/bin/bash
# ----------------------------------------------
# Nightshift Watchdog
#
# Keeps the daemon running forever. If it crashes,
# cleans up and restarts. Designed to run via:
#
# caffeinate -s bash scripts/watchdog.sh codex 60
#
# Or in tmux:
# tmux new-session -d -s nightshift "caffeinate -s bash scripts/watchdog.sh codex 60"
#
# The watchdog never exits unless you kill it.
# The daemon inside it handles its own circuit breaker,
# budget limits, and session caps.
# ----------------------------------------------

set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
LOCKFILE="$REPO_DIR/.nightshift-daemon.lock"

AGENT="${1:-codex}"
PAUSE="${2:-60}"
RESTART_DELAY=30
MAX_RESTARTS_PER_HOUR=5
RESTART_COUNT=0
LAST_RESET_TIME=$(date +%s)

echo "=================================================="
echo " NIGHTSHIFT WATCHDOG"
echo " Agent: $AGENT"
echo " Pause: ${PAUSE}s"
echo " Restart delay: ${RESTART_DELAY}s"
echo " Max restarts: $MAX_RESTARTS_PER_HOUR per hour"
echo " Stop: Ctrl+C"
echo "=================================================="
echo ""

while true; do
# --- Rate limit restarts ---
NOW=$(date +%s)
ELAPSED=$(( NOW - LAST_RESET_TIME ))
if [ "$ELAPSED" -ge 3600 ]; then
RESTART_COUNT=0
LAST_RESET_TIME=$NOW
fi

if [ "$RESTART_COUNT" -ge "$MAX_RESTARTS_PER_HOUR" ]; then
echo "WATCHDOG: $MAX_RESTARTS_PER_HOUR restarts in the last hour. Something is fundamentally broken."
echo "WATCHDOG: Sleeping 1 hour before retrying."
sleep 3600
RESTART_COUNT=0
LAST_RESET_TIME=$(date +%s)
fi

# --- Clean stale lock ---
if [ -d "$LOCKFILE" ]; then
echo "WATCHDOG: Cleaning stale lock from previous crash."
rmdir "$LOCKFILE" 2>/dev/null || true
fi

# --- Run the daemon ---
echo "WATCHDOG: Starting daemon (attempt $((RESTART_COUNT + 1))) --- $(date '+%Y-%m-%d %H:%M:%S') ---"
bash "$SCRIPT_DIR/daemon.sh" "$AGENT" "$PAUSE"
EXIT_CODE=$?

RESTART_COUNT=$((RESTART_COUNT + 1))

if [ "$EXIT_CODE" -eq 0 ]; then
echo "WATCHDOG: Daemon exited cleanly (exit 0). Restarting in ${RESTART_DELAY}s."
else
echo "WATCHDOG: Daemon crashed (exit $EXIT_CODE). Restarting in ${RESTART_DELAY}s."
fi

sleep "$RESTART_DELAY"
done