summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Sima <ben@bsima.me>2025-11-20 17:58:45 -0500
committerBen Sima <ben@bsima.me>2025-11-20 17:58:45 -0500
commit2c4f916861d35038c0b7e1142aab10f149637bf3 (patch)
treeeebc184d07adf4efe90360226371c71f65dee86a
parent93e49d92fd0c3ad07376c8aad091374dcfe484cf (diff)
parent2659f0ffeac500b615813c7a806ab20de7d51ba2 (diff)
Merge branch 'live' into omni-worker-1
-rw-r--r--.tasks/tasks.jsonl2
-rwxr-xr-xBiz/Dragons.hs14
-rw-r--r--Omni/Agent/WORKER_AGENT_GUIDE.md12
-rwxr-xr-xOmni/Agent/start-worker.sh123
4 files changed, 128 insertions, 23 deletions
diff --git a/.tasks/tasks.jsonl b/.tasks/tasks.jsonl
index 4ea9f61..409dc2e 100644
--- a/.tasks/tasks.jsonl
+++ b/.tasks/tasks.jsonl
@@ -127,7 +127,7 @@
{"taskCreatedAt":"2025-11-20T21:41:12.821995769Z","taskDependencies":[],"taskId":"t-1ndDBuq","taskNamespace":"Biz/PodcastItLater.hs","taskParent":"t-143KQl2","taskPriority":"P2","taskStatus":"Open","taskTitle":"PodcastItLater: Enforce Paid Limits in UI","taskType":"WorkTask","taskUpdatedAt":"2025-11-20T21:41:12.821995769Z"}
{"taskCreatedAt":"2025-11-20T21:41:20.029426381Z","taskDependencies":[],"taskId":"t-1ne7Qtj","taskNamespace":"Network/Wai/Middleware/Braid.hs","taskParent":"t-1f9QP23","taskPriority":"P2","taskStatus":"Open","taskTitle":"Implement Braid keep-alive mechanism","taskType":"WorkTask","taskUpdatedAt":"2025-11-20T21:41:20.029426381Z"}
{"taskCreatedAt":"2025-11-20T21:41:20.048368004Z","taskDependencies":[],"taskId":"t-1ne7VoO","taskNamespace":"Biz/Que/Host.hs","taskParent":"t-1f9QP23","taskPriority":"P2","taskStatus":"Open","taskTitle":"Revive authkey authentication in Que/Host","taskType":"WorkTask","taskUpdatedAt":"2025-11-20T21:41:20.048368004Z"}
-{"taskCreatedAt":"2025-11-20T21:41:20.067644599Z","taskDependencies":[],"taskId":"t-1ne80pJ","taskNamespace":"Biz/Dragons.hs","taskParent":"t-1f9QP23","taskPriority":"P2","taskStatus":"Open","taskTitle":"Store generated JWK in persistent file","taskType":"WorkTask","taskUpdatedAt":"2025-11-20T21:41:20.067644599Z"}
+{"taskCreatedAt":"2025-11-20T21:41:20.067644599Z","taskDependencies":[],"taskId":"t-1ne80pJ","taskNamespace":"Biz/Dragons.hs","taskParent":"t-1f9QP23","taskPriority":"P2","taskStatus":"Done","taskTitle":"Store generated JWK in persistent file","taskType":"WorkTask","taskUpdatedAt":"2025-11-20T22:54:17.655700806Z"}
{"taskCreatedAt":"2025-11-20T21:41:32.113815607Z","taskDependencies":[],"taskId":"t-1neWyaO","taskNamespace":"Biz/PodcastItLater.hs","taskParent":"t-144hCMJ","taskPriority":"P2","taskStatus":"Open","taskTitle":"Add tests for Admin dashboard","taskType":"WorkTask","taskUpdatedAt":"2025-11-20T21:41:32.113815607Z"}
{"taskCreatedAt":"2025-11-20T21:41:32.132888832Z","taskDependencies":[],"taskId":"t-1neWD8r","taskNamespace":"Biz/PodcastItLater.hs","taskParent":"t-144hCMJ","taskPriority":"P2","taskStatus":"Open","taskTitle":"Add error handling tests for Worker","taskType":"WorkTask","taskUpdatedAt":"2025-11-20T21:41:32.132888832Z"}
{"taskCreatedAt":"2025-11-20T22:42:03.728732682Z","taskDependencies":[],"taskId":"t-1rcIr6X","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskPriority":"P2","taskStatus":"Open","taskTitle":"Implement 'task progress <epic-id>' command","taskType":"WorkTask","taskUpdatedAt":"2025-11-20T22:42:03.728732682Z"}
diff --git a/Biz/Dragons.hs b/Biz/Dragons.hs
index 7ba7fa0..cfe211e 100755
--- a/Biz/Dragons.hs
+++ b/Biz/Dragons.hs
@@ -742,7 +742,19 @@ startup quiet = do
cfg <- Envy.decodeWithDefaults Envy.defConfig
oAuthArgs <- Envy.decodeWithDefaults Envy.defConfig
kp <- Acid.openLocalStateFrom (keep cfg) init :: IO (Acid.AcidState Keep)
- jwk <- Auth.generateKey -- TODO: store this in a file somewhere
+ let jwkPath = keep cfg </> "jwk.json"
+ jwkExists <- Directory.doesFileExist jwkPath
+ jwk <-
+ if jwkExists
+ then do
+ maybeKey <- Aeson.decodeFileStrict jwkPath
+ case maybeKey of
+ Nothing -> panic <| "Could not decode JWK from " <> str jwkPath
+ Just k -> pure k
+ else do
+ k <- Auth.generateKey
+ Aeson.encodeFile jwkPath k
+ pure k
let url = case homeExample cfg of
ForgeURL u -> u
CLISubmission -> "<CLISubmission>"
diff --git a/Omni/Agent/WORKER_AGENT_GUIDE.md b/Omni/Agent/WORKER_AGENT_GUIDE.md
index ff33259..782d4d5 100644
--- a/Omni/Agent/WORKER_AGENT_GUIDE.md
+++ b/Omni/Agent/WORKER_AGENT_GUIDE.md
@@ -22,10 +22,9 @@ The Worker Agent should run the following loop continuously:
# Go to worker directory
cd ../omni-worker-1
-# Reset base branch to latest live code
-# This ensures we build on top of the latest merged work
-git fetch origin live
-git reset --hard origin/live
+# Update base branch with latest live code
+# We merge the local 'live' branch directly since we share the git dir
+git merge live --no-edit
# Sync tasks from the live branch
./Omni/Agent/sync-tasks.sh
@@ -56,9 +55,10 @@ task update t-123 in-progress
* If you find an unmerged dependency branch, check it out: `git checkout task/t-parent-id`.
* Otherwise, start from fresh live code: `git checkout omni-worker-1` (which tracks `live`).
-4. **Create Feature Branch**:
+4. **Create/Checkout Feature Branch**:
```bash
- git checkout -b task/t-123
+ # Try to switch to existing branch, otherwise create new one
+ git checkout task/t-123 || git checkout -b task/t-123
```
### Step 4: Implement
diff --git a/Omni/Agent/start-worker.sh b/Omni/Agent/start-worker.sh
index e3a7200..d005156 100755
--- a/Omni/Agent/start-worker.sh
+++ b/Omni/Agent/start-worker.sh
@@ -12,9 +12,9 @@ TARGET="${1:-omni-worker-1}"
# The first worktree listed is always the main one
MAIN_REPO="$(git worktree list --porcelain | grep '^worktree ' | head -n 1 | cut -d ' ' -f 2)"
AMP_BIN="$MAIN_REPO/node_modules/.bin/amp"
+TASK_BIN="$MAIN_REPO/_/bin/task"
# 2. Resolve Worker Path
-# If TARGET is a directory, use it. Otherwise assume it's a sibling of MAIN_REPO.
if [ -d "$TARGET" ]; then
WORKER_PATH="$(realpath "$TARGET")"
elif [ -d "$MAIN_REPO/../$TARGET" ]; then
@@ -26,10 +26,14 @@ fi
if [ ! -x "$AMP_BIN" ]; then
echo "Error: Amp binary not found at '$AMP_BIN'."
- echo "Please ensure npm dependencies are installed in the main repository."
exit 1
fi
+# Ensure task binary is built/available
+if [ ! -x "$TASK_BIN" ]; then
+ echo "Warning: Task binary not found at '$TASK_BIN'. Assuming it's in path or build it first."
+fi
+
echo "Starting Worker Agent Loop"
echo " Worker Path: $WORKER_PATH"
echo " Amp Binary: $AMP_BIN"
@@ -37,28 +41,117 @@ echo " Log File: $WORKER_PATH/_/llm/amp.log"
echo " Monitor: tail -f $WORKER_PATH/_/llm/amp.log"
echo " Press Ctrl+C to stop."
+# Function to sync tasks safely
+sync_tasks() {
+ "$MAIN_REPO/Omni/Agent/sync-tasks.sh" "$@"
+}
+
cd "$WORKER_PATH"
# 3. The Worker Loop
while true; do
echo "----------------------------------------------------------------"
- echo "$(date): Starting new agent session..."
-
- mkdir -p _/llm
- "$AMP_BIN" --log-file "_/llm/amp.log" --dangerously-allow-all -x "You are a Worker Agent. Your goal is to process tasks from the task manager.
+ echo "$(date): Syncing and checking for work..."
+
+ # A. Sync with Live
+ # We use 'git merge' to update our base branch from the shared 'live' ref
+ git checkout omni-worker-1 >/dev/null 2>&1
+ git merge live --no-edit >/dev/null 2>&1 || echo "Warning: Merge conflict or issue merging live."
+
+ # B. Sync Tasks
+ sync_tasks
+
+ # C. Find Ready Work
+ # We use jq to parse the first task
+ # Note: task ready --json returns an array [...]
+ TASK_JSON=$("$TASK_BIN" ready --json 2>/dev/null | jq -r '.[0] // empty')
+
+ if [ -z "$TASK_JSON" ]; then
+ echo "$(date): No ready tasks. Sleeping for 60s..."
+ sleep 60
+ continue
+ fi
+
+ TASK_ID=$(echo "$TASK_JSON" | jq -r '.taskId')
+ TASK_TITLE=$(echo "$TASK_JSON" | jq -r '.taskTitle')
+ TASK_NS=$(echo "$TASK_JSON" | jq -r '.taskNamespace // "root"')
+
+ echo "$(date): Claiming task $TASK_ID: $TASK_TITLE"
+
+ # D. Claim Task
+ "$TASK_BIN" update "$TASK_ID" in-progress >/dev/null
+ sync_tasks --commit >/dev/null
+
+ # E. Prepare Branch
+ BRANCH_NAME="task/$TASK_ID"
+ if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then
+ echo "Resuming existing branch $BRANCH_NAME"
+ git checkout "$BRANCH_NAME" >/dev/null
+ else
+ echo "Creating new branch $BRANCH_NAME"
+ git checkout -b "$BRANCH_NAME" >/dev/null
+ fi
+
+ # F. Execute Agent
+ echo "Launching Amp to implement task..."
+
+ TASK_DETAILS=$("$TASK_BIN" show "$TASK_ID")
+
+ # We construct a specific prompt for the agent
+ PROMPT="You are a Worker Agent.
+Your goal is to implement the following task:
- Step 1: Read 'Omni/Agent/WORKER_AGENT_GUIDE.md' to understand your workflow.
- Step 2: Follow the 'Worker Loop' instructions exactly.
+$TASK_DETAILS
- Start by syncing tasks and checking for ready work. If no work is ready, exit."
+INSTRUCTIONS:
+1. Analyze the codebase (use finder/Grep) to understand where to make changes.
+2. Implement the changes by editing files.
+3. Run tests to verify your work (e.g., 'bild --test Omni/Namespace').
+4. Fix any errors found during testing.
+5. Do NOT update the task status or manage git branches (the system handles that).
+6. When finished and tested, exit.
- EXIT_CODE=$?
+Context:
+- You are working in '$WORKER_PATH'.
+- The task is in namespace '$TASK_NS'.
+"
- if [ $EXIT_CODE -ne 0 ]; then
- echo "Agent exited with error code $EXIT_CODE. Sleeping for 10s..."
- sleep 10
+ mkdir -p _/llm
+ "$AMP_BIN" --log-file "_/llm/amp.log" --dangerously-allow-all -x "$PROMPT"
+
+ AGENT_EXIT_CODE=$?
+
+ if [ $AGENT_EXIT_CODE -eq 0 ]; then
+ echo "Agent finished successfully."
+
+ # G. Submit Work
+ if [ -n "$(git status --porcelain)" ]; then
+ echo "Committing changes..."
+ git add .
+ git commit -m "feat: implement $TASK_ID" || true
+ else
+ echo "No changes to commit."
+ fi
+
+ echo "Submitting for review..."
+ # Switch back to base
+ git checkout omni-worker-1 >/dev/null
+
+ # Sync again (in case live moved)
+ git merge live --no-edit >/dev/null 2>&1
+ sync_tasks
+
+ # Update status
+ "$TASK_BIN" update "$TASK_ID" review >/dev/null
+ sync_tasks --commit >/dev/null
+
+ echo "Task $TASK_ID submitted for review."
+
else
- echo "Agent session finished. Sleeping for 5s..."
- sleep 5
+ echo "Agent failed (exit code $AGENT_EXIT_CODE). Sleeping for 10s before retrying..."
+ sleep 10
fi
+
+ echo "Cooldown..."
+ sleep 5
done