Project 29: “The Delegate Background Worker” — Async Workflows
| Attribute | Value |
|---|---|
| File | KIRO_CLI_LEARNING_PROJECTS.md |
| Main Programming Language | Bash |
| Coolness Level | Level 3: Genuinely Clever |
| Difficulty | Level 2: Intermediate |
| Knowledge Area | Async Workflows |
What you’ll build: Use delegate to run tests and fix linting while you keep working.
Why it teaches Parallelism: The agent becomes non-blocking.
Success criteria:
- A background task completes and reports its summary.
Real World Outcome
You will have a Kiro CLI workflow that delegates time-consuming tasks to background agents while you continue working on other tasks. When you run it, you’ll see:
Delegating a Task:
$ kiro "I need to implement user authentication. But first, delegate fixing all ESLint errors in the background."
🔄 Delegating Task: Fix ESLint errors
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Starting background agent: kiro_delegate_001
Task: Fix all ESLint errors in src/ directory
Priority: Low
Estimated time: 10-15 minutes
Background agent running in: /tmp/kiro_delegate_001.log
Agent ID: agent_20240102_143052
[Main session continues - you keep working on auth]
✓ Background task delegated. You can continue working.
Main Session Continues (Non-Blocking):
[You continue in the main conversation]
$ kiro "Add JWT authentication to the Express API"
🔨 Implementing JWT Authentication
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 1: Installing dependencies...
$ npm install jsonwebtoken bcrypt
Step 2: Creating auth middleware...
[Creating src/middleware/auth.js]
Step 3: Adding login endpoint...
[Creating POST /api/auth/login]
[While you work, background agent runs in parallel]
Background Task Completes:
🎉 Background Task Complete: agent_20240102_143052
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Task: Fix ESLint errors
Duration: 12m 34s
Status: Success ✓
Summary of Changes:
├─ Fixed 47 ESLint errors across 12 files
│ ├─ 23 × no-unused-vars (removed dead code)
│ ├─ 15 × prefer-const (changed let → const)
│ ├─ 6 × no-console (added eslint-disable comments)
│ └─ 3 × missing-return (added return statements)
├─ Ran tests: 156 passed, 0 failed
└─ Created commit: "fix: resolve 47 ESLint errors"
Files modified:
M src/controllers/userController.js
M src/services/authService.js
M src/utils/validation.js
... (9 more files)
Full log: /tmp/kiro_delegate_001.log
Checking Background Task Status:
$ kiro "/delegate status"
📋 Active Background Tasks
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
agent_20240102_143052 [COMPLETE] ✓
Task: Fix ESLint errors
Started: 2:30 PM
Completed: 2:43 PM (12m 34s)
Status: Success
agent_20240102_143515 [RUNNING] ⏳
Task: Run integration test suite
Started: 2:35 PM
Elapsed: 8m 15s
Progress: Running test 47/89...
agent_20240102_144201 [FAILED] ✗
Task: Update all dependencies
Started: 2:42 PM
Failed: 2:45 PM (3m 12s)
Error: Dependency conflict in @types/node
Log: /tmp/kiro_delegate_003.log
Monitoring Real-Time Progress:
$ tail -f /tmp/kiro_delegate_002.log
[14:35:12] Starting integration tests...
[14:36:45] ✓ Auth flow tests (12 tests, 2.3s)
[14:37:23] ✓ Database migrations (8 tests, 3.1s)
[14:38:01] ⏳ API endpoint tests (running 47/89...)
[14:38:45] ✓ POST /api/users (201 response, 0.8s)
[14:39:12] ✓ GET /api/users/:id (200 response, 0.5s)
...
You’re seeing exactly what concurrent programming enables - parallelism that lets you stay productive while long-running tasks complete in the background!
The Core Question You’re Answering
“How do you make Kiro non-blocking so you can work on Task A while Task B executes in parallel?”
Before you write any code, sit with this question. Most CLI tools are synchronous (“wait for this to finish before doing anything else”), but modern development workflows demand parallelism:
Synchronous (blocking) workflow:
You: "Run the test suite"
Kiro: [runs 500 tests for 15 minutes]
You: [waits... twiddling thumbs... can't do anything else]
Kiro: "Tests passed!"
You: [finally continues]
Asynchronous (non-blocking) workflow:
You: "Delegate running the test suite to a background agent"
Kiro: [spawns background agent]
Background Agent: [runs 500 tests for 15 minutes in parallel]
You: [continues working on auth implementation]
You: [continues working on API docs]
Background Agent: "Tests passed!" [notifies when done]
This is the same pattern as:
- GitHub Actions (CI/CD in background while you keep coding)
- Background jobs in web apps (Sidekiq, Celery, Bull)
- Async/await in programming (non-blocking I/O)
Concepts You Must Understand First
Stop and research these before coding:
- Process Management (Unix)
- What is a process? (running program with PID)
- How do you spawn a background process in Bash? (
command &,nohup,disown) - What happens when a parent process exits? (orphaned processes, init adoption)
- Book Reference: “Advanced Programming in the UNIX Environment” Ch. 9 (Process Relationships) - Stevens & Rago
- Inter-Process Communication (IPC)
- How do two processes communicate? (pipes, sockets, files, signals)
- What is stdout/stderr redirection? (
>,>>,2>&1) - How do you read a process’s output while it’s running? (
tail -f, named pipes) - Book Reference: “The Linux Programming Interface” Ch. 44 (Pipes and FIFOs) - Michael Kerrisk
- Job Control
- What is job control? (fg, bg, jobs, kill)
- How do you bring a background job to the foreground? (
fg %1) - What signals exist? (SIGTERM, SIGKILL, SIGINT, SIGHUP)
- Book Reference: “Learning the bash Shell” Ch. 8 (Job Control) - Cameron Newham
- Async Execution Patterns
- What is the difference between parallel and concurrent?
- How do you wait for multiple background tasks? (
wait $PID1 $PID2) - What is a task queue? (producers add tasks, workers consume)
- Blog Reference: “Concurrency vs Parallelism” - Rob Pike (Go creator)
- Exit Codes & Error Handling
- What do exit codes mean? (0 = success, 1-255 = error)
- How do you capture a background process’s exit code? (
wait $PID; echo $?) - How do you handle failures in background tasks?
- Docs Reference: Bash manual on exit status
Questions to Guide Your Design
Before implementing, think through these:
- Task Lifecycle
- How do you spawn a background Kiro session? (new process? Docker container? tmux pane?)
- Where do you store task metadata? (PID, log file, status, start time)
- How do you track which tasks are running vs completed?
- Communication Protocol
- How does the main session know when a background task completes?
- File-based polling (check status.json every 5s)?
- Signal-based notification (SIGUSR1 when done)?
- Webhook/HTTP callback?
- Logging & Observability
- Where do background task logs go? (separate file per task? centralized?)
- How do you tail logs in real-time? (
tail -f) - How do you prevent log files from growing unbounded? (rotation, max size)
- Error Handling
- What if a background task crashes? (save stack trace to log)
- What if a background task hangs? (timeout after 1 hour?)
- What if the main session exits while background tasks run? (orphan cleanup?)
- Resource Limits
- How many background tasks can run concurrently? (CPU cores, memory limits)
- Should you queue tasks if too many are running? (max 4 concurrent)
- How do you prioritize tasks? (critical > high > normal > low)
Thinking Exercise
Trace Background Task Execution
Before coding, manually trace this workflow:
Given:
- Main session: User asks Kiro to implement JWT auth
- Background task: Fix ESLint errors (15-minute task)
Trace each step:
- User Delegates Task
$ kiro "Delegate fixing ESLint errors while I work on auth"- Main Kiro session detects “delegate” keyword
- Creates task metadata:
{ "id": "agent_20240102_143052", "task": "Fix ESLint errors", "status": "starting", "pid": null, "log_file": "/tmp/kiro_delegate_001.log", "start_time": "2024-01-02T14:30:52Z" } - Question: Where is this metadata stored? (file? database? in-memory?)
- Spawn Background Process
# Main session executes: nohup kiro "Fix all ESLint errors in src/" > /tmp/kiro_delegate_001.log 2>&1 & BACKGROUND_PID=$! # Update metadata with PID echo $BACKGROUND_PID > /tmp/kiro_delegate_001.pidnohup: Ignore SIGHUP (session logout won’t kill it)&: Run in background$!: Capture PID of last background process- Question: What if the background Kiro process spawns subprocesses? (process tree)
- Main Session Continues (Non-Blocking)
# User continues working $ kiro "Add JWT authentication" # Main session is responsive immediately [Working on auth implementation...]- Background task runs in parallel
- Main session doesn’t wait
- Question: How does Kiro prevent context pollution? (separate conversation history?)
- Background Task Runs
# In the background (separate process): [Background Agent Log - /tmp/kiro_delegate_001.log] [14:30:52] Starting task: Fix ESLint errors [14:31:05] Running: eslint src/ --fix [14:31:45] Fixed 47 errors across 12 files [14:32:10] Running tests: npm test [14:43:15] Tests passed (156/156) [14:43:20] Creating commit: "fix: resolve 47 ESLint errors" [14:43:26] Task complete ✓- Question: How does the background agent know to commit? (task instructions)
- Completion Detection
# Background process writes completion metadata { "id": "agent_20240102_143052", "status": "complete", "exit_code": 0, "end_time": "2024-01-02T14:43:26Z", "summary": "Fixed 47 ESLint errors, tests passed" } # Main session polls or gets notified [Main Session] Background task agent_20240102_143052 completed ✓- Question: Polling (check every 10s) vs event-driven (callback)?
Questions while tracing:
- What if the background task needs user input? (can’t prompt, must fail gracefully)
- What if the background task modifies files the main session is using? (file locking, conflict resolution)
- What if two background tasks both try to commit? (git lock conflict)
The Interview Questions They’ll Ask
Prepare to answer these:
-
“Explain the difference between concurrency and parallelism. How does background task delegation relate to each?”
-
“Your background task hangs indefinitely. How would you implement a timeout mechanism in Bash?”
-
“You have 10 background tasks queued but only 2 CPU cores. How would you schedule them efficiently?”
-
“A background task crashes halfway through. How do you ensure it doesn’t leave the codebase in a broken state?”
-
“Walk me through how
nohup command &works at the OS level. What happens when the parent shell exits?” -
“You’re running 5 background agents. How would you implement a priority queue so critical tasks run first?”
Hints in Layers
Hint 1: Start with Process Spawning Before building the full system, prove you can spawn a background process and capture its output. Test with a simple sleep command:
# Spawn background process
nohup sleep 30 > /tmp/test.log 2>&1 &
BACKGROUND_PID=$!
echo "Started PID: $BACKGROUND_PID"
# Check if it's running
ps -p $BACKGROUND_PID
Hint 2: Store Task Metadata Create a task registry (simple JSON file):
# /tmp/kiro_tasks.json
{
"tasks": [
{
"id": "agent_001",
"pid": 12345,
"status": "running",
"log": "/tmp/kiro_delegate_001.log",
"started": "2024-01-02T14:30:52Z",
"task": "Fix ESLint errors"
}
]
}
Use jq to read/write JSON from Bash.
Hint 3: Delegate Command
Implement a /delegate command in Kiro:
# Pseudocode for /delegate handler
if user_input.startswith("/delegate"):
task_description = extract_task(user_input)
# Create task metadata
task_id = generate_id() # agent_YYYYMMDD_HHMMSS
log_file = f"/tmp/kiro_delegate_{task_id}.log"
# Spawn background Kiro process
pid = spawn_background(f"kiro '{task_description}'", log_file)
# Register task
register_task(task_id, pid, log_file, task_description)
# Notify user
print(f"✓ Delegated task {task_id} (PID {pid})")
Hint 4: Monitor Background Tasks
Implement a /delegate status command:
# Read task registry
tasks = read_json("/tmp/kiro_tasks.json")
for task in tasks:
pid = task['pid']
# Check if process is still running
if process_exists(pid):
status = "RUNNING ⏳"
else:
exit_code = get_exit_code(pid) # from wait $pid
status = "COMPLETE ✓" if exit_code == 0 else "FAILED ✗"
print(f"{task['id']} [{status}] {task['task']}")
Hint 5: Tail Logs in Real-Time Allow users to monitor background tasks:
# Command: kiro "/delegate logs agent_001"
log_file = get_log_file("agent_001")
subprocess.run(f"tail -f {log_file}")
Hint 6: Wait for Completion Implement a blocking wait if needed:
# Command: kiro "/delegate wait agent_001"
pid = get_pid("agent_001")
wait $pid # Blocks until process exits
exit_code = $?
if [ $exit_code -eq 0 ]; then
echo "✓ Task completed successfully"
else
echo "✗ Task failed with exit code $exit_code"
fi
Hint 7: Cleanup Orphaned Tasks When main session exits, decide what to do with background tasks:
# Option 1: Kill all background tasks
trap 'kill $(jobs -p)' EXIT
# Option 2: Let them continue (orphan them)
disown -a # Remove from job table
# Option 3: Ask user
echo "Background tasks running. Kill them? (y/n)"
Hint 8: Prevent Context Pollution Each background agent should have an isolated conversation history:
# Spawn with fresh context
kiro --new-session "Fix ESLint errors"
# Or use explicit context isolation
kiro --context-id "delegate_001" "Fix ESLint errors"
Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Process Management & Job Control | “Advanced Programming in the UNIX Environment” by Stevens & Rago | Ch. 9-10 |
| Inter-Process Communication | “The Linux Programming Interface” by Michael Kerrisk | Ch. 44 (Pipes), Ch. 63 (Sockets) |
| Bash Background Jobs | “Learning the bash Shell” by Cameron Newham | Ch. 8 (Job Control) |
| Signals & Process Control | “Linux System Programming” by Robert Love | Ch. 5 (Process Management) |
| Concurrency Patterns | “The Art of Concurrency” by Clay Breshears | Ch. 2 (Threads vs Processes) |
Common Pitfalls & Debugging
Problem 1: “Background task exits immediately after spawning”
- Why: The background process inherits stdin/stdout tied to the terminal, which closes when the parent exits.
- Fix: Use
nohupand redirect all I/O:nohup command > log.txt 2>&1 & - Quick test: Logout and login again, then
ps aux | grep kiro- background process should still be running.
Problem 2: “Can’t read background task’s PID after spawning”
- Why:
$!only works immediately after&. If you run other commands,$!changes. - Fix: Capture PID immediately:
command & PID=$!; echo $PID > task.pid - Quick test:
cat task.pidshould show the correct PID, verify withps -p $(cat task.pid).
Problem 3: “Background task writes to main session’s stdout (pollutes output)”
- Why: Background process still has stdout pointing to the terminal.
- Fix: Redirect stdout/stderr to a log file:
command > /tmp/task.log 2>&1 & - Quick test: Main session should have clean output, logs go to file only.
Problem 4: “Background task hangs forever, no timeout”
- Why: No timeout mechanism in place.
- Fix: Use
timeoutcommand:timeout 1h kiro "long task" &(kills after 1 hour) - Quick test:
timeout 5s sleep 10 &- process should die after 5 seconds.
Problem 5: “Background task conflicts with main session (file locks, git operations)”
- Why: Both sessions try to modify the same files or run
git commitsimultaneously. - Fix: Implement file locking (
flock) or coordinate via task queue (only one git operation at a time). - Quick test: Start two background tasks that both commit - second should wait or fail gracefully.
Definition of Done
- Delegate command works:
/delegate "run tests"spawns a background Kiro process - PID is captured: Background task’s PID is stored and accessible
- Logs are isolated: Each background task writes to its own log file
- Main session is non-blocking: User can continue working immediately after delegating
- Status command works:
/delegate statusshows running/completed/failed tasks - Real-time monitoring:
tail -fon log files shows live progress - Completion detection: Main session notifies when background tasks finish
- Exit code handling: Failed background tasks are marked as failed (non-zero exit code)
- Orphan cleanup: Background tasks don’t become zombies when main session exits
- Context isolation: Background tasks don’t pollute main session’s conversation history