diff --git a/.claude/commands/prd.md b/.claude/commands/prd.md index 794fbf8..50af39b 100644 --- a/.claude/commands/prd.md +++ b/.claude/commands/prd.md @@ -51,6 +51,52 @@ Ask for project slug. Check if `projects/{name}` exists: - If exists: "Project exists. Continue editing or choose different name?" - If new: proceed +## Step 2.5: Check for Duplicate Work (Distributed Tracking) + +Before starting discovery, check if work already exists in the target repo: + +```bash +# Check if distributed tracking exists in target repo +TARGET_REPO="${TARGET_REPO:-$(pwd)}" +if [ -f "$TARGET_REPO/.hq/prd.json" ]; then + cat "$TARGET_REPO/.hq/prd.json" +fi +``` + +**If `.hq/prd.json` exists:** + +1. Parse and show existing tasks: + ``` + Found existing distributed work in this repo: + + | ID | Title | Status | + |----|-------|--------| + | US-001 | Example task | ✓ complete | + | US-002 | Another task | pending | + ``` + +2. Fuzzy match user's description against existing task titles: + - Normalize: lowercase, remove punctuation + - Check for: exact match, contains match, word overlap > 50% + - If match found: + ``` + ⚠️ Potential duplicate detected: + Your request: "add user authentication" + Existing task: "US-003: Add user auth flow" (pending) + + Similarity: 78% word overlap + ``` + +3. Ask user before proceeding: + ``` + Options: + A. Add to existing project (extend .hq/prd.json) + B. Create separate project (new projects/{name}/) + C. Cancel and review existing work first + ``` + +**If no `.hq/prd.json` exists:** Proceed to Step 3. + ## Step 3: Discovery Interview Ask questions in batches. Format: diff --git a/.claude/commands/sync-tasks.md b/.claude/commands/sync-tasks.md new file mode 100644 index 0000000..39e7c91 --- /dev/null +++ b/.claude/commands/sync-tasks.md @@ -0,0 +1,254 @@ +# /sync-tasks - Manual Distributed Tracking Sync + +Manual sync command for checking task status, pushing updates, and claiming tasks in distributed projects. + +## Usage + +``` +/sync-tasks {project} # Pull and show status diff +/sync-tasks {project} --push # Push local PRD to repo +/sync-tasks {project} --claim {id} # Claim a specific task +/sync-tasks {project} --release {id} # Release a claim +``` + +## Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `{project}` | Yes | Project name (folder name in `projects/`) | +| `--push` | No | Push local PRD to target repo's `.hq/` | +| `--claim {id}` | No | Claim a specific task ID | +| `--release {id}` | No | Release a claim on a task ID | + +## Steps + +### Default: Pull and Show Status Diff + +1. **Read local PRD:** + ``` + projects/{project}/prd.json + ``` + +2. **Get target repo from PRD:** + Extract `target_repo` field from the PRD. + +3. **Check for distributed tracking:** + ```bash + if [ -f "{target_repo}/.hq/prd.json" ]; then + echo "Distributed tracking found" + else + echo "No distributed tracking in target repo" + echo "Use --push to initialize" + exit 0 + fi + ``` + +4. **Compare PRDs and show diff:** + ``` + SYNC STATUS: {project} + ┌─────────────────────────────────────────────────────────────┐ + │ Local PRD: projects/{project}/prd.json │ + │ Repo PRD: {target_repo}/.hq/prd.json │ + │ Last sync: {sync_metadata.synced_at} │ + ├─────────────────────────────────────────────────────────────┤ + │ TASK STATUS COMPARISON │ + │ │ + │ ID │ Title │ Local │ Repo │ │ + │ ────────┼──────────────────────────┼────────┼────────┼───── │ + │ US-001 │ Define .hq/ structure │ PASS │ PASS │ = │ + │ US-002 │ Add push-to-repo │ PASS │ PASS │ = │ + │ US-003 │ Add pull-from-repo │ FAIL │ PASS │ ← │ + │ US-008 │ Add /sync-tasks │ - │ FAIL │ NEW │ + │ │ + │ Legend: = same, ← repo newer, → local newer, NEW = only one │ + ├─────────────────────────────────────────────────────────────┤ + │ CLAIMS │ + │ │ + │ US-006: claimed by pure-ralph-WORK (expires in 22h) │ + │ US-010: claimed by stefan (expires in 2h) │ + ├─────────────────────────────────────────────────────────────┤ + │ SUMMARY │ + │ Local: 5/10 tasks complete │ + │ Repo: 7/10 tasks complete │ + │ Diff: 2 tasks differ, 0 conflicts │ + └─────────────────────────────────────────────────────────────┘ + ``` + +5. **If differences found, suggest actions:** + ``` + Suggested actions: + - /sync-tasks {project} --push # Push local changes to repo + - /sync-tasks {project} --claim US-008 # Claim a task before starting + ``` + +### With `--push`: Push Local to Repo + +1. **Read local PRD** from `projects/{project}/prd.json` + +2. **Create .hq/ directory if missing:** + ```bash + mkdir -p {target_repo}/.hq + ``` + +3. **Add sync metadata:** + ```json + { + ...full PRD..., + "sync_metadata": { + "synced_at": "{ISO 8601 now}", + "synced_from": "projects/{project}/prd.json", + "synced_by": "sync-tasks-command" + } + } + ``` + +4. **Write to repo:** + ``` + {target_repo}/.hq/prd.json + ``` + +5. **Report result:** + ``` + PUSH COMPLETE + ┌─────────────────────────────────────────────────────────────┐ + │ Pushed: projects/{project}/prd.json │ + │ To: {target_repo}/.hq/prd.json │ + │ At: {synced_at} │ + │ │ + │ Tasks synced: 10 │ + │ Complete: 7, Remaining: 3 │ + └─────────────────────────────────────────────────────────────┘ + + Note: Changes not committed. Run: + git add {target_repo}/.hq/prd.json + git commit -m "sync: update distributed tracking" + ``` + +### With `--claim {task-id}`: Claim a Task + +1. **Verify task exists** in local PRD + +2. **Read or create claims.json:** + ```bash + claims_path="{target_repo}/.hq/claims.json" + if [ ! -f "$claims_path" ]; then + echo '{"claims": [], "updated_at": null}' > "$claims_path" + fi + ``` + +3. **Check if already claimed:** + - If claimed by someone else (not expired): show warning, ask to proceed + - If expired or unclaimed: proceed + +4. **Add claim:** + ```json + { + "task_id": "{task-id}", + "claimed_by": "{username or identifier}", + "claimed_at": "{ISO 8601 now}", + "expires_at": "{ISO 8601 now + 24 hours}", + "notes": "Claimed via /sync-tasks command" + } + ``` + +5. **Write updated claims.json** + +6. **Report result:** + ``` + CLAIM RECORDED + ┌─────────────────────────────────────────────────────────────┐ + │ Task: {task-id} - {task title} │ + │ Claimed: {claimed_at} │ + │ Expires: {expires_at} │ + │ By: {claimed_by} │ + └─────────────────────────────────────────────────────────────┘ + + Note: Commit the claim: + git add {target_repo}/.hq/claims.json + git commit -m "claim: {task-id}" + ``` + +### With `--release {task-id}`: Release a Claim + +1. **Read claims.json** + +2. **Find and remove the claim** for the specified task ID + +3. **Write updated claims.json** + +4. **Report result:** + ``` + CLAIM RELEASED + ┌─────────────────────────────────────────────────────────────┐ + │ Task: {task-id} - {task title} │ + │ Released: {ISO 8601 now} │ + └─────────────────────────────────────────────────────────────┘ + ``` + +## Comparison Logic + +When comparing local and repo PRDs: + +| Field | Compare? | Notes | +|-------|----------|-------| +| `id` | Key | Match tasks by ID | +| `passes` | Yes | Status changes are important | +| `notes` | Yes | Implementation details | +| `updated_at` | Yes | Determines which is newer | +| `title` | No | Minor wording doesn't matter | +| `description` | No | Details don't affect matching | + +### Diff Symbols + +| Symbol | Meaning | +|--------|---------| +| `=` | Same in both | +| `←` | Repo is newer (has updates) | +| `→` | Local is newer | +| `NEW` | Only exists in one | +| `!` | Conflict (both modified) | + +## Error Handling + +| Error | Response | +|-------|----------| +| Project not found | "Project '{project}' not found in projects/" | +| No target_repo in PRD | "PRD missing target_repo field" | +| Task ID not found | "Task '{id}' not found in PRD" | +| Already claimed | Show claim info, ask to override | + +## Examples + +**Check sync status:** +``` +> /sync-tasks distributed-tracking + +SYNC STATUS: distributed-tracking +...shows diff table... +``` + +**Push local changes:** +``` +> /sync-tasks distributed-tracking --push + +PUSH COMPLETE +Pushed 10 tasks to C:/my-hq/.hq/prd.json +``` + +**Claim a task:** +``` +> /sync-tasks distributed-tracking --claim US-008 + +CLAIM RECORDED +Task: US-008 - Add /sync-tasks slash command +Claimed by: stefan +Expires: 2026-01-28T15:30:00Z +``` + +**Release a claim:** +``` +> /sync-tasks distributed-tracking --release US-008 + +CLAIM RELEASED +Task: US-008 - Add /sync-tasks slash command +``` diff --git a/.claude/scripts/pure-ralph-loop.ps1 b/.claude/scripts/pure-ralph-loop.ps1 index 97cd512..c8fe10b 100644 --- a/.claude/scripts/pure-ralph-loop.ps1 +++ b/.claude/scripts/pure-ralph-loop.ps1 @@ -40,11 +40,15 @@ param( # Configuration # ============================================================================ -$BasePromptPath = Join-Path $HqPath "prompts/pure-ralph-base.md" +$HqBasePromptPath = Join-Path $HqPath "prompts/pure-ralph-base.md" $ProjectName = (Split-Path (Split-Path $PrdPath -Parent) -Leaf) $LogDir = Join-Path $HqPath "workspace/orchestrator/$ProjectName" $LogFile = Join-Path $LogDir "pure-ralph.log" -$LockFile = Join-Path $TargetRepo ".pure-ralph.lock" + +# .hq/ directory paths in target repo +$HqDir = Join-Path $TargetRepo ".hq" +$RepoPromptPath = Join-Path $HqDir "prompt.md" +$RepoPrdPath = Join-Path $HqDir "prd.json" # Create log directory New-Item -ItemType Directory -Path $LogDir -Force | Out-Null @@ -53,6 +57,52 @@ New-Item -ItemType Directory -Path $LogDir -Force | Out-Null # Functions # ============================================================================ +function Initialize-HqDirectory { + # Create .hq directory if missing + if (-not (Test-Path $HqDir)) { + Write-Log "Creating .hq directory in target repo" + New-Item -ItemType Directory -Path $HqDir -Force | Out-Null + } + + # Copy prompt.md if missing + if (-not (Test-Path $RepoPromptPath)) { + Write-Log "Copying prompt template to $RepoPromptPath" + if (Test-Path $HqBasePromptPath) { + # Read the base prompt and substitute TARGET_REPO + $promptContent = Get-Content $HqBasePromptPath -Raw + $promptContent = $promptContent -replace '\{\{TARGET_REPO\}\}', $TargetRepo + $promptContent | Out-File -FilePath $RepoPromptPath -Encoding utf8 + Write-Log "Prompt template created at $RepoPromptPath" "SUCCESS" + } else { + Write-Log "Base prompt not found at $HqBasePromptPath" "ERROR" + exit 1 + } + } else { + Write-Log "Using existing prompt at $RepoPromptPath" + } + + # Copy prd.json if missing + if (-not (Test-Path $RepoPrdPath)) { + Write-Log "Copying PRD to $RepoPrdPath" + if (Test-Path $PrdPath) { + # Read the HQ PRD and add sync_metadata + $prdContent = Get-Content $PrdPath -Raw | ConvertFrom-Json + $prdContent | Add-Member -NotePropertyName "sync_metadata" -NotePropertyValue @{ + synced_at = (Get-Date -Format "o") + synced_from = $PrdPath + synced_by = "pure-ralph-init" + } -Force + $prdContent | ConvertTo-Json -Depth 10 | Out-File -FilePath $RepoPrdPath -Encoding utf8 + Write-Log "PRD copied to $RepoPrdPath" "SUCCESS" + } else { + Write-Log "HQ PRD not found at $PrdPath" "ERROR" + exit 1 + } + } else { + Write-Log "Using existing PRD at $RepoPrdPath" + } +} + function Write-Log { param([string]$Message, [string]$Level = "INFO") $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" @@ -67,87 +117,9 @@ function Write-Log { } } -function Create-LockFile { - $lockContent = @{ - project = $ProjectName - pid = $PID - started_at = (Get-Date -Format "o") - } | ConvertTo-Json - $lockContent | Out-File -FilePath $LockFile -Encoding utf8 - Write-Log "Lock file created: $LockFile" -} - -function Remove-LockFile { - if (Test-Path $LockFile) { - Remove-Item -Path $LockFile -Force - Write-Log "Lock file removed: $LockFile" - } -} - -function Check-ExistingLock { - if (Test-Path $LockFile) { - try { - $lockContent = Get-Content $LockFile -Raw | ConvertFrom-Json - $lockProject = $lockContent.project - $lockPid = $lockContent.pid - $lockStarted = [DateTime]::Parse($lockContent.started_at) - $duration = (Get-Date) - $lockStarted - $durationStr = "{0:hh\:mm\:ss}" -f $duration - - Write-Host "" - Write-Host "=== WARNING: Lock File Detected ===" -ForegroundColor Yellow - Write-Host "Another pure-ralph loop may be running on this repo." -ForegroundColor Yellow - Write-Host "" - Write-Host " Project: $lockProject" -ForegroundColor Gray - Write-Host " PID: $lockPid" -ForegroundColor Gray - Write-Host " Started: $($lockContent.started_at)" -ForegroundColor Gray - Write-Host " Duration: $durationStr" -ForegroundColor Gray - Write-Host "" - - # Check if process is still running - $processRunning = $false - try { - $proc = Get-Process -Id $lockPid -ErrorAction SilentlyContinue - if ($proc) { - $processRunning = $true - Write-Host " Process Status: RUNNING" -ForegroundColor Red - } else { - Write-Host " Process Status: NOT RUNNING (stale lock)" -ForegroundColor Yellow - } - } catch { - Write-Host " Process Status: NOT RUNNING (stale lock)" -ForegroundColor Yellow - } - Write-Host "" - - Write-Log "Existing lock file found for project '$lockProject' (PID: $lockPid, Duration: $durationStr)" "WARN" - - # Prompt user - $response = Read-Host "Another pure-ralph is running. Continue anyway? (y/N)" - if ($response -match "^[Yy]$") { - Write-Log "User chose to continue despite existing lock" "WARN" - Write-Host "Continuing... (existing lock will be overwritten)" -ForegroundColor Yellow - return $true - } else { - Write-Log "User chose to abort due to existing lock" "INFO" - Write-Host "Aborting." -ForegroundColor Red - return $false - } - } catch { - Write-Log "Could not parse lock file: $_" "WARN" - # If we can't parse the lock file, ask the user - $response = Read-Host "Lock file exists but couldn't be read. Continue anyway? (y/N)" - if ($response -match "^[Yy]$") { - return $true - } else { - return $false - } - } - } - return $true -} - function Get-TaskProgress { - $prd = Get-Content $PrdPath -Raw | ConvertFrom-Json + # Read from repo's .hq/prd.json + $prd = Get-Content $RepoPrdPath -Raw | ConvertFrom-Json $total = $prd.features.Count $complete = ($prd.features | Where-Object { $_.passes -eq $true }).Count return @{ Total = $total; Complete = $complete; Remaining = $total - $complete } @@ -156,10 +128,9 @@ function Get-TaskProgress { function Build-Prompt { param([bool]$IsManual) - # Read base prompt and substitute only PRD_PATH and TARGET_REPO - $prompt = Get-Content $BasePromptPath -Raw - $prompt = $prompt -replace '\{\{PRD_PATH\}\}', $PrdPath - $prompt = $prompt -replace '\{\{TARGET_REPO\}\}', $TargetRepo + # Read prompt from repo's .hq/prompt.md (already has TARGET_REPO substituted) + # No PRD_PATH substitution needed - prompt references .hq/prd.json directly + $prompt = Get-Content $RepoPromptPath -Raw # In manual mode, add instruction for user to close window if ($IsManual) { @@ -204,27 +175,20 @@ Write-Log "PRD: $PrdPath" Write-Log "Target: $TargetRepo" Write-Log "Mode: $modeLabel" -# Check for existing lock file (conflict detection) -if (-not (Check-ExistingLock)) { - exit 1 -} - -# Create lock file to prevent concurrent execution -Create-LockFile +# Initialize .hq directory (copy prompt.md and prd.json if missing) +Initialize-HqDirectory -# Ensure lock file is removed on exit (success or failure) -try { - # Build the prompt ONCE (only PRD_PATH and TARGET_REPO substituted) - $prompt = Build-Prompt -IsManual $Manual - $promptFile = Join-Path $LogDir "current-prompt.md" - $prompt | Out-File -FilePath $promptFile -Encoding utf8 +# Build the prompt from .hq/prompt.md (no PRD_PATH substitution needed) +$prompt = Build-Prompt -IsManual $Manual +$promptFile = Join-Path $LogDir "current-prompt.md" +$prompt | Out-File -FilePath $promptFile -Encoding utf8 - Write-Log "Prompt built and saved to $promptFile" +Write-Log "Prompt built and saved to $promptFile" - $iteration = 0 - $maxIterations = 50 +$iteration = 0 +$maxIterations = 50 - while ($iteration -lt $maxIterations) { +while ($iteration -lt $maxIterations) { $iteration++ $progress = Get-TaskProgress @@ -293,20 +257,15 @@ exit Start-Sleep -Seconds 2 } - if ($iteration -ge $maxIterations) { - Write-Log "Safety limit reached ($maxIterations iterations)" "WARN" - } +if ($iteration -ge $maxIterations) { + Write-Log "Safety limit reached ($maxIterations iterations)" "WARN" +} - # Final summary - $progress = Get-TaskProgress - Write-Host "" - Write-Host "=== Final Summary ===" -ForegroundColor Cyan - Write-Host "Completed: $($progress.Complete)/$($progress.Total) tasks" -ForegroundColor $(if ($progress.Remaining -eq 0) { "Green" } else { "Yellow" }) - Write-Host "Log: $LogFile" -ForegroundColor Gray +# Final summary +$progress = Get-TaskProgress +Write-Host "" +Write-Host "=== Final Summary ===" -ForegroundColor Cyan +Write-Host "Completed: $($progress.Complete)/$($progress.Total) tasks" -ForegroundColor $(if ($progress.Remaining -eq 0) { "Green" } else { "Yellow" }) +Write-Host "Log: $LogFile" -ForegroundColor Gray - Write-Log "Loop ended. Final: $($progress.Complete)/$($progress.Total) complete" -} -finally { - # Always remove lock file on exit (success or failure) - Remove-LockFile -} +Write-Log "Loop ended. Final: $($progress.Complete)/$($progress.Total) complete" diff --git a/.claude/scripts/pure-ralph-loop.sh b/.claude/scripts/pure-ralph-loop.sh index b619683..ff9f695 100644 --- a/.claude/scripts/pure-ralph-loop.sh +++ b/.claude/scripts/pure-ralph-loop.sh @@ -109,11 +109,15 @@ fi # Configuration # ============================================================================ -BASE_PROMPT_PATH="$HQ_PATH/prompts/pure-ralph-base.md" +HQ_BASE_PROMPT_PATH="$HQ_PATH/prompts/pure-ralph-base.md" PROJECT_NAME=$(basename "$(dirname "$PRD_PATH")") LOG_DIR="$HQ_PATH/workspace/orchestrator/$PROJECT_NAME" LOG_FILE="$LOG_DIR/pure-ralph.log" -LOCK_FILE="$TARGET_REPO/.pure-ralph.lock" + +# .hq/ directory paths in target repo +HQ_DIR="$TARGET_REPO/.hq" +REPO_PROMPT_PATH="$HQ_DIR/prompt.md" +REPO_PRD_PATH="$HQ_DIR/prd.json" # ============================================================================ # Color Output @@ -126,6 +130,53 @@ CYAN='\033[0;36m' GRAY='\033[0;90m' NC='\033[0m' # No Color +# ============================================================================ +# HQ Directory Initialization +# ============================================================================ + +initialize_hq_directory() { + # Create .hq directory if missing + if [[ ! -d "$HQ_DIR" ]]; then + write_log "Creating .hq directory in target repo" + mkdir -p "$HQ_DIR" + fi + + # Copy prompt.md if missing + if [[ ! -f "$REPO_PROMPT_PATH" ]]; then + write_log "Copying prompt template to $REPO_PROMPT_PATH" + if [[ -f "$HQ_BASE_PROMPT_PATH" ]]; then + # Read the base prompt and substitute TARGET_REPO + sed "s|{{TARGET_REPO}}|$TARGET_REPO|g" "$HQ_BASE_PROMPT_PATH" > "$REPO_PROMPT_PATH" + write_log "Prompt template created at $REPO_PROMPT_PATH" "SUCCESS" + else + write_log "Base prompt not found at $HQ_BASE_PROMPT_PATH" "ERROR" + exit 1 + fi + else + write_log "Using existing prompt at $REPO_PROMPT_PATH" + fi + + # Copy prd.json if missing + if [[ ! -f "$REPO_PRD_PATH" ]]; then + write_log "Copying PRD to $REPO_PRD_PATH" + if [[ -f "$PRD_PATH" ]]; then + # Read the HQ PRD and add sync_metadata using jq + local timestamp + timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + jq --arg synced_at "$timestamp" \ + --arg synced_from "$PRD_PATH" \ + '. + {sync_metadata: {synced_at: $synced_at, synced_from: $synced_from, synced_by: "pure-ralph-init"}}' \ + "$PRD_PATH" > "$REPO_PRD_PATH" + write_log "PRD copied to $REPO_PRD_PATH" "SUCCESS" + else + write_log "HQ PRD not found at $PRD_PATH" "ERROR" + exit 1 + fi + else + write_log "Using existing PRD at $REPO_PRD_PATH" + fi +} + # ============================================================================ # Logging Functions # ============================================================================ @@ -147,108 +198,6 @@ initialize_logging() { } >> "$LOG_FILE" } -# ============================================================================ -# Lock File Functions -# ============================================================================ - -create_lock_file() { - local timestamp - timestamp=$(date -Iseconds) - cat > "$LOCK_FILE" </dev/null || echo "unknown") - lock_pid=$(jq -r '.pid' "$LOCK_FILE" 2>/dev/null || echo "unknown") - lock_started=$(jq -r '.started_at' "$LOCK_FILE" 2>/dev/null || echo "unknown") - - # Calculate duration if we can parse the timestamp - local duration_str="unknown" - if [[ "$lock_started" != "unknown" ]]; then - local start_epoch - local now_epoch - local diff_seconds - # Try to parse ISO timestamp - if command -v gdate &> /dev/null; then - start_epoch=$(gdate -d "$lock_started" +%s 2>/dev/null || echo "0") - else - start_epoch=$(date -d "$lock_started" +%s 2>/dev/null || echo "0") - fi - now_epoch=$(date +%s) - if [[ "$start_epoch" != "0" ]]; then - diff_seconds=$((now_epoch - start_epoch)) - local hours=$((diff_seconds / 3600)) - local minutes=$(((diff_seconds % 3600) / 60)) - local seconds=$((diff_seconds % 60)) - duration_str=$(printf "%02d:%02d:%02d" $hours $minutes $seconds) - fi - fi - - echo "" - echo -e "${YELLOW}=== WARNING: Lock File Detected ===${NC}" - echo -e "${YELLOW}Another pure-ralph loop may be running on this repo.${NC}" - echo "" - echo -e "${GRAY} Project: $lock_project${NC}" - echo -e "${GRAY} PID: $lock_pid${NC}" - echo -e "${GRAY} Started: $lock_started${NC}" - echo -e "${GRAY} Duration: $duration_str${NC}" - echo "" - - # Check if process is still running - local process_running=false - if [[ "$lock_pid" != "unknown" ]] && kill -0 "$lock_pid" 2>/dev/null; then - process_running=true - echo -e " Process Status: ${RED}RUNNING${NC}" - else - echo -e " Process Status: ${YELLOW}NOT RUNNING (stale lock)${NC}" - fi - echo "" - - write_log "Existing lock file found for project '$lock_project' (PID: $lock_pid, Duration: $duration_str)" "WARN" - - # Prompt user - read -r -p "Another pure-ralph is running. Continue anyway? (y/N) " response - case "$response" in - [Yy]) - write_log "User chose to continue despite existing lock" "WARN" - echo -e "${YELLOW}Continuing... (existing lock will be overwritten)${NC}" - return 0 - ;; - *) - write_log "User chose to abort due to existing lock" "INFO" - echo -e "${RED}Aborting.${NC}" - return 1 - ;; - esac - fi - return 0 -} - -# Trap to ensure lock file is removed on exit (success or failure) -cleanup_on_exit() { - remove_lock_file -} -trap cleanup_on_exit EXIT - write_log() { local message="$1" local level="${2:-INFO}" @@ -291,11 +240,12 @@ check_jq() { } get_prd() { - if [[ ! -f "$PRD_PATH" ]]; then - write_log "PRD not found: $PRD_PATH" "ERROR" + # Read from repo's .hq/prd.json + if [[ ! -f "$REPO_PRD_PATH" ]]; then + write_log "PRD not found: $REPO_PRD_PATH" "ERROR" exit 1 fi - cat "$PRD_PATH" + cat "$REPO_PRD_PATH" } get_task_count() { @@ -343,14 +293,15 @@ build_task_prompt() { local task="$1" local prd="$2" - # Read base prompt - if [[ ! -f "$BASE_PROMPT_PATH" ]]; then - write_log "Base prompt not found: $BASE_PROMPT_PATH" "ERROR" + # Read prompt from repo's .hq/prompt.md (already has TARGET_REPO substituted) + # No PRD_PATH substitution needed - prompt references .hq/prd.json directly + if [[ ! -f "$REPO_PROMPT_PATH" ]]; then + write_log "Prompt not found: $REPO_PROMPT_PATH" "ERROR" exit 1 fi local base_prompt - base_prompt=$(cat "$BASE_PROMPT_PATH") + base_prompt=$(cat "$REPO_PROMPT_PATH") # Extract task details local task_id @@ -358,10 +309,8 @@ build_task_prompt() { task_id=$(echo "$task" | jq -r '.id') task_title=$(echo "$task" | jq -r '.title') - # Replace placeholders + # Only substitute task-specific placeholders (TARGET_REPO already done during init) local prompt="$base_prompt" - prompt="${prompt//\{\{TARGET_REPO\}\}/$TARGET_REPO}" - prompt="${prompt//\{\{PRD_PATH\}\}/$PRD_PATH}" prompt="${prompt//\{\{TASK_ID\}\}/$task_id}" prompt="${prompt//\{\{TASK_TITLE\}\}/$task_title}" @@ -373,7 +322,7 @@ build_task_prompt() { cat </dev/null || \ start_ralph_loop() { initialize_logging - # Check for existing lock file (conflict detection) - if ! check_existing_lock; then - exit 1 - fi - - # Create lock file to prevent concurrent execution - create_lock_file - echo "" echo -e "${CYAN}=== Pure Ralph Loop ===${NC}" echo -e "${GRAY}PRD: $PRD_PATH${NC}" echo -e "${GRAY}Target: $TARGET_REPO${NC}" echo -e "${GRAY}Log: $LOG_FILE${NC}" + # Initialize .hq directory (copy prompt.md and prd.json if missing) + initialize_hq_directory + # Check for beads CLI availability if check_beads_cli; then echo -e "${GRAY}Beads CLI: ${GREEN}available${NC}" diff --git a/.hq/prd.json b/.hq/prd.json new file mode 100644 index 0000000..6656def --- /dev/null +++ b/.hq/prd.json @@ -0,0 +1,162 @@ +{ + "project": "distributed-tracking", + "goal": "Sync PRD/task status to target repos so distributed teams have visibility and don't duplicate work", + "success_criteria": "Contributors can see planned work, claim tasks, and status updates propagate via git", + "target_repo": "C:/my-hq", + "has_ui": false, + "features": [ + { + "id": "US-001", + "title": "Define .hq/ directory structure", + "description": "Document standard location in repos for distributed tracking files", + "acceptance_criteria": [ + "knowledge/distributed-tracking/structure.md exists", + "Documents .hq/ directory with: prd.json, claims.json, sync-log.json", + "Each file has JSON schema defined", + "Documents .gitignore recommendations" + ], + "files": ["knowledge/distributed-tracking/structure.md"], + "passes": true, + "notes": "Created structure.md with JSON schemas for prd.json, claims.json, sync-log.json. Includes .gitignore recommendations." + }, + { + "id": "US-002", + "title": "Add push-to-repo function", + "description": "Push PRD/task status to target repo's .hq/ directory", + "acceptance_criteria": [ + "prompts/pure-ralph-base.md includes push_to_repo instructions", + "Copies PRD data to {target_repo}/.hq/prd.json", + "Includes: full PRD, task status, worker assignments, notes", + "Adds synced_at timestamp and synced_from field", + "Creates .hq/ directory if missing", + "Commits with message: sync: update distributed tracking" + ], + "files": ["prompts/pure-ralph-base.md"], + "dependsOn": ["US-001"], + "passes": true, + "notes": "Added 'Distributed Tracking - Push to Repo' section to pure-ralph-base.md. Documents push_to_repo function with: mkdir for .hq dir, sync_metadata with synced_at/synced_from/synced_by, what gets synced (full PRD, features, status, notes), example, and commit strategy options." + }, + { + "id": "US-003", + "title": "Add pull-from-repo function", + "description": "Pull latest status from repo before starting work", + "acceptance_criteria": [ + "prompts/pure-ralph-base.md includes pull_from_repo instructions", + "Reads {target_repo}/.hq/prd.json if exists", + "Compares with local projects/{name}/prd.json", + "Returns diff summary (tasks added, changed, removed)", + "Does NOT auto-overwrite local" + ], + "files": ["prompts/pure-ralph-base.md"], + "dependsOn": ["US-001"], + "passes": true, + "notes": "Worker: knowledge-curator. Selection reason: Documentation task adding instructions to prompt file. Added 'Distributed Tracking - Pull from Repo' section with: pull_from_repo function steps, diff detection logic comparing by task ID, comparison table for fields, example output format, and explicit no-auto-overwrite policy." + }, + { + "id": "US-004", + "title": "Add conflict detection and merge", + "description": "Detect conflicts between local and repo PRD, merge intelligently", + "acceptance_criteria": [ + "prompts/pure-ralph-base.md includes conflict resolution instructions", + "When local and repo differ: show task-level diff", + "Merge strategy: per-task, newer updated_at wins", + "User prompted to confirm merge", + "Merged result written to both local and repo" + ], + "files": ["prompts/pure-ralph-base.md"], + "dependsOn": ["US-003"], + "passes": false, + "notes": "" + }, + { + "id": "US-005", + "title": "Add duplicate work detection", + "description": "Warn when planning work that already exists in repo", + "acceptance_criteria": [ + ".claude/commands/prd.md includes duplicate check step", + "On PRD creation: check if .hq/prd.json exists in target repo", + "If exists: show existing tasks, ask add or create separate", + "Fuzzy match task titles to catch near-duplicates", + "Warning shown before PRD is saved" + ], + "files": [".claude/commands/prd.md"], + "dependsOn": ["US-001"], + "passes": false, + "notes": "" + }, + { + "id": "US-006", + "title": "Add task claim mechanism", + "description": "Claim tasks before starting to prevent conflicts", + "acceptance_criteria": [ + "prompts/pure-ralph-base.md includes claim instructions", + "claims.json schema: task_id, claimed_by, claimed_at, expires_at", + "Before task: check if claimed by someone else", + "If claimed: show warning with claimer info and timestamp", + "Claims expire after 24h by default", + "On task complete: release claim" + ], + "files": ["prompts/pure-ralph-base.md", "knowledge/distributed-tracking/structure.md"], + "dependsOn": ["US-001"], + "passes": false, + "notes": "" + }, + { + "id": "US-007", + "title": "Integrate with pure-ralph loop", + "description": "Automatic sync during pure-ralph execution", + "acceptance_criteria": [ + "prompts/pure-ralph-base.md updated with sync workflow", + "On loop start: pull from repo, warn on conflicts", + "Before each task: check claim, claim if available", + "After each task: push status to repo", + "On loop complete: push final, release claims" + ], + "files": ["prompts/pure-ralph-base.md"], + "dependsOn": ["US-002", "US-003", "US-006"], + "passes": false, + "notes": "" + }, + { + "id": "US-008", + "title": "Add /sync-tasks slash command", + "description": "Manual sync command for ad-hoc status checks", + "acceptance_criteria": [ + ".claude/commands/sync-tasks.md exists", + "/sync-tasks {project} pulls and shows status diff", + "/sync-tasks {project} --push pushes local to repo", + "/sync-tasks {project} --claim {task-id} claims specific task", + "Shows clear summary of sync result" + ], + "files": [".claude/commands/sync-tasks.md"], + "dependsOn": ["US-002", "US-003", "US-006"], + "passes": false, + "notes": "" + }, + { + "id": "US-009", + "title": "Document distributed workflow", + "description": "Comprehensive documentation for contributors", + "acceptance_criteria": [ + "knowledge/distributed-tracking/workflow.md exists", + "Documents full sync lifecycle", + "Includes troubleshooting for common conflicts", + "Includes examples of claim/release flow" + ], + "files": ["knowledge/distributed-tracking/workflow.md"], + "dependsOn": ["US-007"], + "passes": false, + "notes": "" + } + ], + "metadata": { + "created_at": "2026-01-27T12:00:00Z", + "created_by": "stefan", + "purpose": "Enable distributed teams to share PRD/task status via git" + }, + "sync_metadata": { + "synced_at": "2026-01-27T08:42:58-08:00", + "synced_from": "C:/my-hq/projects/distributed-tracking/prd.json", + "synced_by": "pure-ralph" + } +} diff --git a/knowledge/distributed-tracking/structure.md b/knowledge/distributed-tracking/structure.md new file mode 100644 index 0000000..7295c60 --- /dev/null +++ b/knowledge/distributed-tracking/structure.md @@ -0,0 +1,283 @@ +# Distributed Tracking Directory Structure + +The `.hq/` directory enables distributed teams to share PRD and task status via git. + +## Directory Location + +``` +{target_repo}/ +├── .hq/ +│ ├── prd.json # Project requirements and task status +│ ├── claims.json # Active task claims +│ ├── sync-log.json # Sync history +│ └── prompt.md # Pure Ralph prompt (copied from HQ, can evolve with project) +├── src/ +└── ... +``` + +## File Schemas + +### prd.json + +Synced copy of project PRD with tracking metadata. + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": ["project", "goal", "features", "sync_metadata"], + "properties": { + "project": { + "type": "string", + "description": "Project identifier" + }, + "goal": { + "type": "string", + "description": "High-level project goal" + }, + "success_criteria": { + "type": "string", + "description": "How success is measured" + }, + "has_ui": { + "type": "boolean", + "description": "Whether project has UI components requiring Playwright testing" + }, + "features": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "title", "passes"], + "properties": { + "id": { + "type": "string", + "description": "Unique task identifier (e.g., US-001)" + }, + "title": { + "type": "string", + "description": "Task title" + }, + "description": { + "type": "string", + "description": "Detailed task description" + }, + "acceptance_criteria": { + "type": "array", + "items": { "type": "string" }, + "description": "Verifiable completion criteria" + }, + "files": { + "type": "array", + "items": { "type": "string" }, + "description": "Files to create or modify" + }, + "dependsOn": { + "type": "array", + "items": { "type": "string" }, + "description": "Task IDs that must complete first" + }, + "passes": { + "type": ["boolean", "null"], + "description": "Task completion status" + }, + "notes": { + "type": "string", + "description": "Implementation notes, failure details" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "Last modification timestamp" + } + } + } + }, + "sync_metadata": { + "type": "object", + "required": ["synced_at", "synced_from"], + "properties": { + "synced_at": { + "type": "string", + "format": "date-time", + "description": "When this sync occurred" + }, + "synced_from": { + "type": "string", + "description": "Source HQ path (e.g., C:/my-hq/projects/distributed-tracking)" + }, + "synced_by": { + "type": "string", + "description": "User or agent who performed sync" + } + } + }, + "metadata": { + "type": "object", + "properties": { + "created_at": { "type": "string", "format": "date-time" }, + "created_by": { "type": "string" }, + "purpose": { "type": "string" } + } + } + } +} +``` + +### claims.json + +Tracks which tasks are claimed by which contributor to prevent duplicate work. + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": ["claims"], + "properties": { + "claims": { + "type": "array", + "items": { + "type": "object", + "required": ["task_id", "claimed_by", "claimed_at", "expires_at"], + "properties": { + "task_id": { + "type": "string", + "description": "Task ID being claimed (e.g., US-001)" + }, + "claimed_by": { + "type": "string", + "description": "Identifier of claimer (user, agent, or machine)" + }, + "claimed_at": { + "type": "string", + "format": "date-time", + "description": "When claim was made" + }, + "expires_at": { + "type": "string", + "format": "date-time", + "description": "When claim expires (default: 24h from claimed_at)" + }, + "notes": { + "type": "string", + "description": "Optional context about the claim" + } + } + } + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "Last modification to claims file" + } + } +} +``` + +### sync-log.json + +Audit trail of all sync operations for debugging and history. + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": ["entries"], + "properties": { + "entries": { + "type": "array", + "items": { + "type": "object", + "required": ["timestamp", "action", "source"], + "properties": { + "timestamp": { + "type": "string", + "format": "date-time", + "description": "When operation occurred" + }, + "action": { + "type": "string", + "enum": ["push", "pull", "claim", "release", "merge"], + "description": "Type of sync operation" + }, + "source": { + "type": "string", + "description": "Origin of the operation" + }, + "target": { + "type": "string", + "description": "Destination of the operation" + }, + "tasks_affected": { + "type": "array", + "items": { "type": "string" }, + "description": "Task IDs involved in this operation" + }, + "details": { + "type": "string", + "description": "Additional context or error info" + }, + "actor": { + "type": "string", + "description": "User or agent who performed operation" + } + } + } + } + } +} +``` + +### prompt.md + +The Pure Ralph prompt file, copied from HQ and customizable per project. + +```markdown +# Pure Ralph Prompt + +You are executing the Pure Ralph Loop... + +**PRD Path:** .hq/prd.json +**Target Repo:** {{TARGET_REPO}} + +--- + +[Full prompt content - see prompts/pure-ralph-base.md] +``` + +**Key Characteristics:** +- **Initial source:** Copied from `HQ/prompts/pure-ralph-base.md` on first run +- **References PRD locally:** Uses `.hq/prd.json` (not an HQ path) +- **Project-specific evolution:** Can be edited to add project-specific patterns +- **Version controlled:** Part of the repo, evolves with the project + +**Why prompt.md lives in .hq/:** +1. **Portability:** Project can run Ralph loop without HQ access +2. **Customization:** Project teams can tune the prompt for their needs +3. **Version tracking:** Prompt changes are committed with the project +4. **Self-contained:** Everything needed for distributed work is in one directory + +## .gitignore Recommendations + +Add to the target repository's `.gitignore`: + +```gitignore +# Distributed tracking - DO NOT ignore +# .hq/ + +# Local-only files (if needed) +.hq/*.local.json +.hq/scratch/ +``` + +**Important:** The `.hq/` directory should be committed to git. This is intentional - the whole point is sharing status via git. Only ignore: + +- `*.local.json` - Local overrides not meant for sharing +- `scratch/` - Temporary working files + +## Usage + +1. **First sync:** Creates `.hq/` directory with initial prd.json +2. **Subsequent syncs:** Updates prd.json, manages claims +3. **Contributors:** Pull repo, check `.hq/prd.json` for available tasks +4. **Claim before work:** Update claims.json to prevent conflicts +5. **After completion:** Push updated status, release claim diff --git a/knowledge/distributed-tracking/workflow.md b/knowledge/distributed-tracking/workflow.md new file mode 100644 index 0000000..e17db27 --- /dev/null +++ b/knowledge/distributed-tracking/workflow.md @@ -0,0 +1,383 @@ +# Distributed Tracking Workflow Guide + +Comprehensive guide for contributors using distributed tracking to collaborate on projects via git. + +## Overview + +Distributed tracking enables multiple contributors (human or AI) to work on the same project without duplicating work. It uses the `.hq/` directory in target repos to share: +- **PRD status** - Which tasks are done, in progress, or available +- **Claims** - Who is working on what +- **Sync history** - Audit trail of all sync operations + +## Full Sync Lifecycle + +### Session Start + +``` +1. BRANCH VERIFICATION + ├── Check current branch: git branch --show-current + ├── Expected: feature/{project-name} + └── If wrong: checkout or create feature branch + +2. PULL FROM REPO + ├── Check if {target_repo}/.hq/prd.json exists + ├── Compare with local projects/{project}/prd.json + ├── Generate diff summary + └── If conflicts → resolve before continuing + +3. READ PRD + └── Load local PRD (now synced with repo) +``` + +### Task Selection + +``` +4. FIND ELIGIBLE TASKS + ├── Filter: passes=false or null + ├── Filter: all dependsOn tasks have passes=true + └── Result: list of tasks ready for work + +5. CHECK CLAIMS + ├── For each eligible task: + │ └── Read {target_repo}/.hq/claims.json + │ ├── If claimed (not expired) → mark "claimed by X" + │ └── If available → mark "available" + └── Display task list with claim status + +6. SELECT AND CLAIM + ├── Prefer unclaimed tasks + ├── Select one task + └── Write claim to claims.json (expires in 24h) +``` + +### Implementation + +``` +7. DO THE WORK + ├── Implement the task + ├── Follow acceptance criteria + └── Test your changes + +8. UPDATE PRD + ├── Set passes: true + ├── Add notes with worker info and summary + └── Add updated_at timestamp (ISO 8601) +``` + +### Session End + +``` +9. PUSH TO REPO + ├── Write updated PRD to {target_repo}/.hq/prd.json + └── Include sync_metadata + +10. RELEASE CLAIM + └── Remove your claim from claims.json + +11. COMMIT + ├── Stage task files + ├── Stage .hq/prd.json and .hq/claims.json + └── Commit: "feat({task-id}): Brief description" + +12. CHECK COMPLETION + ├── If more tasks remain → EXIT (loop continues) + └── If all complete → final push, create PR +``` + +## Quick Reference: Commands + +| Phase | Action | Command/File | +|-------|--------|--------------| +| Start | Check branch | `git branch --show-current` | +| Start | Pull status | `/sync-tasks {project}` | +| Select | View claims | Check `.hq/claims.json` | +| Select | Claim task | `/sync-tasks {project} --claim {id}` | +| End | Push status | `/sync-tasks {project} --push` | +| End | Release claim | `/sync-tasks {project} --release {id}` | + +## Claim/Release Flow + +### Claiming a Task + +Before starting any task, claim it to prevent others from working on it simultaneously. + +**Step 1: Check if task is claimed** +``` +Read {target_repo}/.hq/claims.json +Find claim where task_id matches your selected task +Check if expires_at > now (still valid) +``` + +**Step 2: If claimed by someone else** +``` +WARNING: Task US-006 is claimed + +Claimed by: pure-ralph-WORKSTATION +Claimed at: 2026-01-27T14:30:00Z (2 hours ago) +Expires at: 2026-01-28T14:30:00Z (in 22 hours) + +Options: + [S] Skip - pick a different task + [W] Wait - task may be released soon + [O] Override - claim anyway (use with caution) +``` + +**Step 3: If available, create claim** +```json +{ + "task_id": "US-006", + "claimed_by": "pure-ralph-LAPTOP-ABC", + "claimed_at": "2026-01-27T16:00:00Z", + "expires_at": "2026-01-28T16:00:00Z", + "notes": "Working on: Add task claim mechanism" +} +``` + +**Step 4: Write claim and commit** +```bash +# Write updated claims.json +# Then commit: +git add {target_repo}/.hq/claims.json +git commit -m "claim: US-006 by pure-ralph-LAPTOP-ABC" +``` + +### Releasing a Claim + +After completing a task, release your claim so others know the task is done. + +**Step 1: Remove claim from array** +``` +Read claims.json +Remove entry where task_id matches completed task +Update claims.updated_at to now +``` + +**Step 2: Include in task commit** +```bash +# Stage with your task changes: +git add {task_files} {target_repo}/.hq/prd.json {target_repo}/.hq/claims.json +git commit -m "feat(US-006): Add task claim mechanism" +``` + +### Claim Expiration + +Claims automatically expire after 24 hours. This prevents indefinite blocking if: +- A contributor abandons work +- A session crashes +- Someone forgets to release + +**Expired claims are treated as "available"** - no manual cleanup needed. + +### Example: Full Claim Lifecycle + +``` +SESSION START +├── Check claims.json +│ └── US-006: claimed by stefan (expires in 2h) +│ └── US-007: no claim (available) +│ +├── Select US-007 (available) +│ +├── Claim US-007: +│ { +│ "task_id": "US-007", +│ "claimed_by": "pure-ralph-DESKTOP", +│ "claimed_at": "2026-01-27T18:00:00Z", +│ "expires_at": "2026-01-28T18:00:00Z" +│ } +│ +├── WORK ON TASK +│ └── Implement, test, update PRD +│ +├── Release claim: +│ └── Remove US-007 from claims array +│ +└── Commit: + git add src/feature.ts .hq/prd.json .hq/claims.json + git commit -m "feat(US-007): Integrate with pure-ralph loop" +``` + +## Troubleshooting Common Conflicts + +### Conflict: Task Status Differs + +**Symptom:** Local shows `passes: false`, repo shows `passes: true` + +**Cause:** Another contributor completed the task while you were working. + +**Resolution:** +1. Check `updated_at` timestamps on both versions +2. If repo is newer → Accept repo version (task is done) +3. If local is newer → Your work may overwrite; verify the task isn't actually done +4. If timestamps equal or missing → Manual review required + +``` +CONFLICT: US-003 - Add pull-from-repo function +┌──────────────┬─────────────────┬─────────────────┐ +│ Field │ Local │ Repo │ +├──────────────┼─────────────────┼─────────────────┤ +│ passes │ false │ true │ +│ notes │ (empty) │ "Worker: ..." │ +│ updated_at │ - │ 2026-01-27T14:30│ +└──────────────┴─────────────────┴─────────────────┘ + +Repo has timestamp, local doesn't → Accept repo version +``` + +### Conflict: Notes Differ + +**Symptom:** Both local and repo have different `notes` content. + +**Cause:** Two contributors worked on the same task or updated notes independently. + +**Resolution:** +1. Compare `updated_at` - newer wins +2. If both have valuable info, manually merge the notes +3. Write merged result to both locations + +### Conflict: New Tasks in Repo + +**Symptom:** Repo has task IDs that don't exist locally. + +**Cause:** Someone added tasks to the repo PRD (scope expansion). + +**Resolution:** +1. Review new tasks - do they make sense for the project? +2. Add to local PRD if valid +3. Or ignore if they're out of scope for your work + +``` +Tasks added in repo (not in local): + - US-011: "Add retry logic for sync failures" + - US-012: "Improve error messages" + +Action: Add to local? [y/n/review] +``` + +### Conflict: Claimed Task You Need + +**Symptom:** The task you want to work on is claimed by someone else. + +**Cause:** Another contributor is actively working on it. + +**Resolution Options:** +1. **Skip** - Pick a different unclaimed task +2. **Wait** - Check back later (claim may be released) +3. **Override** - Claim anyway if you're sure they're not working on it + +**When to Override:** +- Claim is about to expire (< 1 hour remaining) +- You've communicated with the claimer and they agreed +- The claimer is known to be unavailable + +**When NOT to Override:** +- Claim was made recently (< 12 hours ago) +- You haven't tried to contact the claimer +- Multiple tasks are available - pick another + +### Conflict: Git Merge Conflict on .hq/ Files + +**Symptom:** Git merge conflict in `.hq/prd.json` or `.hq/claims.json` + +**Cause:** Two contributors pushed changes to the same file simultaneously. + +**Resolution for prd.json:** +1. Open the file with conflict markers +2. For each conflicted task, compare `updated_at` +3. Keep the newer version of each task +4. Resolve and commit + +**Resolution for claims.json:** +1. Combine both claim arrays (remove duplicates by task_id) +2. For duplicate task_ids, keep the newer claim +3. Remove any expired claims +4. Resolve and commit + +```bash +# After resolving: +git add .hq/prd.json .hq/claims.json +git commit -m "merge: resolve distributed tracking conflicts" +``` + +### Conflict: Stale Local PRD + +**Symptom:** Your local PRD is significantly behind the repo version. + +**Cause:** You haven't synced in a while; others have completed many tasks. + +**Resolution:** +1. Run `/sync-tasks {project}` to see the full diff +2. If many tasks differ, consider accepting repo state wholesale +3. Apply merge: repo version for completed tasks, keep local for your in-progress work + +### Session Crashed: Claim Not Released + +**Symptom:** You claimed a task but your session crashed before releasing. + +**Impact:** The claim will expire in 24 hours automatically. + +**If you need to work on it sooner:** +1. Manually edit `{target_repo}/.hq/claims.json` +2. Remove your stale claim +3. Re-claim if continuing work + +```bash +# Edit claims.json, remove your stale claim, then: +git add .hq/claims.json +git commit -m "fix: release stale claim from crashed session" +``` + +### Session Crashed: PRD Not Pushed + +**Symptom:** You completed work but the session crashed before pushing to repo. + +**Impact:** Your local PRD has the update, but repo doesn't. + +**Resolution:** +1. Your work is still in local PRD +2. Next session will run `/sync-tasks --push` +3. Conflict resolution will merge if someone else also worked + +## Best Practices + +### Before Starting Work + +1. Always sync first: `/sync-tasks {project}` +2. Review the diff - understand what's changed +3. Pick unclaimed tasks when possible +4. Claim your task before coding + +### During Work + +1. Work on ONE task at a time +2. Don't hold claims longer than needed +3. If blocked, release claim and pick another task + +### After Completing Work + +1. Update PRD with notes AND `updated_at` +2. Push to repo immediately +3. Release your claim +4. Include `.hq/` files in your commit + +### For Distributed Teams + +1. Communicate about large refactors +2. Check sync status before long sessions +3. Don't override claims without good reason +4. Keep sessions short - claim, work, release + +## File Reference + +| File | Purpose | Location | +|------|---------|----------| +| Local PRD | Source of truth for project | `projects/{project}/prd.json` | +| Repo PRD | Shared status for team | `{target_repo}/.hq/prd.json` | +| Claims | Who's working on what | `{target_repo}/.hq/claims.json` | +| Sync Log | Audit trail | `{target_repo}/.hq/sync-log.json` | + +## See Also + +- [Directory Structure](structure.md) - File schemas and directory layout +- `/sync-tasks` command - Manual sync operations +- `prompts/pure-ralph-base.md` - Automated workflow integration diff --git a/projects/distributed-tracking/prd.json b/projects/distributed-tracking/prd.json new file mode 100644 index 0000000..d932043 --- /dev/null +++ b/projects/distributed-tracking/prd.json @@ -0,0 +1,184 @@ +{ + "project": "distributed-tracking", + "goal": "Sync PRD/task status to target repos so distributed teams have visibility and don't duplicate work", + "success_criteria": "Contributors can see planned work, claim tasks, and status updates propagate via git", + "target_repo": "C:/my-hq", + "has_ui": false, + "features": [ + { + "id": "US-001", + "title": "Define .hq/ directory structure", + "description": "Document standard location in repos for distributed tracking files", + "acceptance_criteria": [ + "knowledge/distributed-tracking/structure.md exists", + "Documents .hq/ directory with: prd.json, claims.json, sync-log.json", + "Each file has JSON schema defined", + "Documents .gitignore recommendations" + ], + "files": ["knowledge/distributed-tracking/structure.md"], + "passes": true, + "notes": "Created structure.md with JSON schemas for prd.json, claims.json, sync-log.json. Includes .gitignore recommendations." + }, + { + "id": "US-002", + "title": "Add push-to-repo function", + "description": "Push PRD/task status to target repo's .hq/ directory", + "acceptance_criteria": [ + "prompts/pure-ralph-base.md includes push_to_repo instructions", + "Copies PRD data to {target_repo}/.hq/prd.json", + "Includes: full PRD, task status, worker assignments, notes", + "Adds synced_at timestamp and synced_from field", + "Creates .hq/ directory if missing", + "Commits with message: sync: update distributed tracking" + ], + "files": ["prompts/pure-ralph-base.md"], + "dependsOn": ["US-001"], + "passes": true, + "notes": "Added 'Distributed Tracking - Push to Repo' section to pure-ralph-base.md. Documents push_to_repo function with: mkdir for .hq dir, sync_metadata with synced_at/synced_from/synced_by, what gets synced (full PRD, features, status, notes), example, and commit strategy options." + }, + { + "id": "US-003", + "title": "Add pull-from-repo function", + "description": "Pull latest status from repo before starting work", + "acceptance_criteria": [ + "prompts/pure-ralph-base.md includes pull_from_repo instructions", + "Reads {target_repo}/.hq/prd.json if exists", + "Compares with local projects/{name}/prd.json", + "Returns diff summary (tasks added, changed, removed)", + "Does NOT auto-overwrite local" + ], + "files": ["prompts/pure-ralph-base.md"], + "dependsOn": ["US-001"], + "passes": true, + "notes": "Worker: knowledge-curator. Selection reason: Documentation task adding instructions to prompt file. Added 'Distributed Tracking - Pull from Repo' section with: pull_from_repo function steps, diff detection logic comparing by task ID, comparison table for fields, example output format, and explicit no-auto-overwrite policy." + }, + { + "id": "US-004", + "title": "Add conflict detection and merge", + "description": "Detect conflicts between local and repo PRD, merge intelligently", + "acceptance_criteria": [ + "prompts/pure-ralph-base.md includes conflict resolution instructions", + "When local and repo differ: show task-level diff", + "Merge strategy: per-task, newer updated_at wins", + "User prompted to confirm merge", + "Merged result written to both local and repo" + ], + "files": ["prompts/pure-ralph-base.md"], + "dependsOn": ["US-003"], + "passes": true, + "notes": "Worker: knowledge-curator. Selection reason: Documentation task adding instructions to prompt file. Added 'Distributed Tracking - Conflict Resolution' section with: task-level diff display format, merge strategy (per-task, newer updated_at wins), updated_at requirement documentation, user confirmation prompt with y/n/manual options, manual resolution flow, write-to-both-locations process, example merge flow, and conflict states table." + }, + { + "id": "US-005", + "title": "Add duplicate work detection", + "description": "Warn when planning work that already exists in repo", + "acceptance_criteria": [ + ".claude/commands/prd.md includes duplicate check step", + "On PRD creation: check if .hq/prd.json exists in target repo", + "If exists: show existing tasks, ask add or create separate", + "Fuzzy match task titles to catch near-duplicates", + "Warning shown before PRD is saved" + ], + "files": [".claude/commands/prd.md"], + "dependsOn": ["US-001"], + "passes": true, + "notes": "Added Step 2.5 'Check for Duplicate Work (Distributed Tracking)' to prd.md. Includes: bash script to check for .hq/prd.json, task table display, fuzzy matching logic (normalize, exact/contains/word overlap >50%), and A/B/C prompt before proceeding." + }, + { + "id": "US-006", + "title": "Add task claim mechanism", + "description": "Claim tasks before starting to prevent conflicts", + "acceptance_criteria": [ + "prompts/pure-ralph-base.md includes claim instructions", + "claims.json schema: task_id, claimed_by, claimed_at, expires_at", + "Before task: check if claimed by someone else", + "If claimed: show warning with claimer info and timestamp", + "Claims expire after 24h by default", + "On task complete: release claim" + ], + "files": ["prompts/pure-ralph-base.md", "knowledge/distributed-tracking/structure.md"], + "dependsOn": ["US-001"], + "passes": true, + "notes": "Worker: knowledge-curator. Selection reason: Documentation task adding instructions to prompt file. Added 'Distributed Tracking - Task Claims' section with: check_claim function (reads claims.json, checks expiration, shows warning), claim_task function (generates claim record, 24h expiration), release_claim function (removes claim on completion), claim identifier formats, warning display format, and integration with task selection flow. claims.json schema was already defined in structure.md." + }, + { + "id": "US-007", + "title": "Integrate with pure-ralph loop", + "description": "Automatic sync during pure-ralph execution", + "acceptance_criteria": [ + "prompts/pure-ralph-base.md updated with sync workflow", + "On loop start: pull from repo, warn on conflicts", + "Before each task: check claim, claim if available", + "After each task: push status to repo", + "On loop complete: push final, release claims" + ], + "files": ["prompts/pure-ralph-base.md"], + "dependsOn": ["US-002", "US-003", "US-006"], + "passes": true, + "notes": "Worker: knowledge-curator. Selection reason: Documentation task adding workflow instructions to prompt file. Added 'Distributed Tracking - Integrated Workflow' section with: session lifecycle diagram, LOOP_START (pull + conflicts), BEFORE_TASK (check claims, display status, claim), AFTER_TASK (push + release), LOOP_COMPLETE (final push, verify claims, create PR), error recovery table, and quick reference table. Updated 'Your Job (Every Session)' section to include sync, claim, push, and release steps.", + "updated_at": "2026-01-27T19:30:00Z" + }, + { + "id": "US-008", + "title": "Add /sync-tasks slash command", + "description": "Manual sync command for ad-hoc status checks", + "acceptance_criteria": [ + ".claude/commands/sync-tasks.md exists", + "/sync-tasks {project} pulls and shows status diff", + "/sync-tasks {project} --push pushes local to repo", + "/sync-tasks {project} --claim {task-id} claims specific task", + "Shows clear summary of sync result" + ], + "files": [".claude/commands/sync-tasks.md"], + "dependsOn": ["US-002", "US-003", "US-006"], + "passes": true, + "notes": "Worker: knowledge-curator. Selection reason: Documentation/command file creation task. Created sync-tasks.md with: default pull-and-diff mode showing task status comparison table, --push flag for pushing local to repo with sync_metadata, --claim and --release flags for task claim management, clear summary output format, comparison logic documentation, and error handling.", + "updated_at": "2026-01-27T20:00:00Z" + }, + { + "id": "US-009", + "title": "Document distributed workflow", + "description": "Comprehensive documentation for contributors", + "acceptance_criteria": [ + "knowledge/distributed-tracking/workflow.md exists", + "Documents full sync lifecycle", + "Includes troubleshooting for common conflicts", + "Includes examples of claim/release flow" + ], + "files": ["knowledge/distributed-tracking/workflow.md"], + "dependsOn": ["US-007"], + "passes": true, + "notes": "Worker: knowledge-curator. Selection reason: Documentation task creating workflow guide. Created comprehensive workflow.md with: full sync lifecycle diagram (session start to end), claim/release flow examples with JSON snippets, troubleshooting section for 7 common conflict scenarios, best practices for distributed teams, and file reference table.", + "updated_at": "2026-01-27T21:00:00Z" + }, + { + "id": "US-010", + "title": "Add prompt templating to .hq/ directory", + "description": "Move Ralph prompt into target repo so it can evolve with the project. PRD path is no longer injected - prompt reads from .hq/prd.json directly.", + "acceptance_criteria": [ + "knowledge/distributed-tracking/structure.md updated to include prompt.md in .hq/ directory", + ".claude/scripts/pure-ralph-loop.ps1 checks for .hq/prompt.md in target repo", + "If .hq/prompt.md missing: copy from HQ/prompts/pure-ralph-base.md to target repo", + "Script uses .hq/prompt.md instead of HQ prompt", + "Prompt references .hq/prd.json directly (no {{PRD_PATH}} substitution needed)", + ".claude/scripts/pure-ralph-loop.sh has same behavior", + "prompts/pure-ralph-base.md updated to use .hq/prd.json path instead of {{PRD_PATH}}" + ], + "files": [ + "knowledge/distributed-tracking/structure.md", + ".claude/scripts/pure-ralph-loop.ps1", + ".claude/scripts/pure-ralph-loop.sh", + "prompts/pure-ralph-base.md" + ], + "dependsOn": ["US-001"], + "passes": true, + "notes": "Worker: knowledge-curator. Selection reason: Documentation and script modification task. Updated structure.md to document prompt.md in .hq/ directory with explanation of portability benefits. Updated pure-ralph-loop.ps1 and pure-ralph-loop.sh with Initialize-HqDirectory/initialize_hq_directory functions that: (1) create .hq/ directory if missing, (2) copy prompt.md from HQ with TARGET_REPO substituted, (3) copy prd.json from HQ with sync_metadata added. Updated prompts/pure-ralph-base.md to reference .hq/prd.json directly instead of {{PRD_PATH}}. Scripts now use repo's .hq/prompt.md and .hq/prd.json for all operations.", + "updated_at": "2026-01-27T22:00:00Z" + } + ], + "metadata": { + "created_at": "2026-01-27T12:00:00Z", + "created_by": "stefan", + "purpose": "Enable distributed teams to share PRD/task status via git" + } +} diff --git a/prompts/pure-ralph-base.md b/prompts/pure-ralph-base.md index 8c52978..c8c3fd2 100644 --- a/prompts/pure-ralph-base.md +++ b/prompts/pure-ralph-base.md @@ -2,7 +2,7 @@ You are executing the Pure Ralph Loop. Read the PRD, pick ONE task, complete it, update the PRD. -**PRD Path:** {{PRD_PATH}} +**PRD Path:** .hq/prd.json **Target Repo:** {{TARGET_REPO}} --- @@ -31,54 +31,6 @@ Extract the project name from the PRD path (e.g., `projects/my-feature/prd.json` --- -## Conflict Awareness - -Pure Ralph sessions may run concurrently. A lock file prevents conflicts. - -### Lock File Location - -``` -{target_repo}/.pure-ralph.lock -``` - -### On Session Start: Check for Lock File - -After switching to the feature branch, check if a lock file exists: - -```bash -LOCK_FILE="{{TARGET_REPO}}/.pure-ralph.lock" -if [ -f "$LOCK_FILE" ]; then - echo "WARNING: Lock file detected" - cat "$LOCK_FILE" -fi -``` - -### If Lock File Found - -1. **Read the lock file** to see which project owns it: - ```json - {"project": "other-project", "pid": 12345, "started_at": "2026-01-26T..."} - ``` - -2. **Check if the process is still running:** - - **Process running:** Another Pure Ralph is active. You should WAIT or inform the user. - - **Process NOT running:** This is a **stale lock**. Safe to remove and continue. - -3. **Removing a stale lock:** - ```bash - # Only if process is NOT running - rm "{{TARGET_REPO}}/.pure-ralph.lock" - ``` - -### Important Notes - -- The orchestrator script creates/removes lock files automatically -- Claude sessions don't create lock files - they only CHECK for them -- If you see a lock from your OWN project (same project name), it's expected - the orchestrator is managing it -- Only worry about locks from DIFFERENT projects on the same repo - ---- - ## Commit Safety **HARD BLOCK: Never commit to main/master** @@ -109,14 +61,17 @@ This is a **HARD BLOCK**, not a warning. Committing to main is NEVER acceptable ## Your Job (Every Session) 1. **BRANCH** - Ensure you're on `feature/{{PROJECT_NAME}}` (create if needed) -2. **READ** the PRD at {{PRD_PATH}} -3. **PICK** the highest priority incomplete task (where `passes` is false/null and dependencies are met) -4. **IMPLEMENT** that ONE task -5. **UPDATE** the PRD: set `passes: true` and fill in `notes` with what you did -6. **COMMIT** with message: `feat(TASK-ID): Brief description` -7. **CHECK** if all tasks complete: - - **If more tasks remain:** EXIT - the loop will spawn a fresh session - - **If all tasks complete:** CREATE PR (see "PR Creation" section below), then EXIT +2. **SYNC** - Pull from repo, resolve any conflicts (see "Distributed Tracking - Integrated Workflow") +3. **READ** the PRD at .hq/prd.json +4. **PICK** the highest priority incomplete task (check claims, prefer unclaimed tasks) +5. **CLAIM** the selected task before starting work +6. **IMPLEMENT** that ONE task +7. **UPDATE** the PRD: set `passes: true`, fill in `notes`, add `updated_at` timestamp +8. **PUSH** - Push status to repo, release claim +9. **COMMIT** with message: `feat(TASK-ID): Brief description` (include `.hq/` files) +10. **CHECK** if all tasks complete: + - **If more tasks remain:** EXIT - the loop will spawn a fresh session + - **If all tasks complete:** Final push, CREATE PR (see "PR Creation" section below), then EXIT --- @@ -404,6 +359,737 @@ Beyond the required worker audit info, notes can include: --- +## Distributed Tracking - Push to Repo + +After completing a task and updating the PRD, push the status to the target repo's `.hq/` directory so distributed teams have visibility. + +### push_to_repo Function + +Execute this after each successful task completion (after PRD update, before or as part of commit). + +Since the prompt now reads directly from .hq/prd.json, changes are already in the repo. The push_to_repo function ensures: +1. The .hq/prd.json has sync_metadata for tracking +2. Changes are committed + +``` +PUSH_TO_REPO: + 1. Ensure .hq/prd.json has sync_metadata: + { + ...full PRD contents..., + "sync_metadata": { + "synced_at": "", + "synced_from": ".hq/prd.json", + "synced_by": "pure-ralph" + } + } + + 2. If sync_metadata doesn't exist, add it + + 3. Update sync_metadata.synced_at to current timestamp + + 4. Stage .hq/prd.json for commit (included with task commit) +``` + +### What Gets Synced + +The `.hq/prd.json` includes: +- **Full PRD:** project, goal, success_criteria, has_ui +- **All features:** with id, title, description, acceptance_criteria +- **Task status:** passes (true/false/null), notes, updated_at +- **Dependencies:** dependsOn arrays +- **Files:** list of files each task touches +- **Metadata:** original created_at, created_by, purpose +- **Sync metadata:** synced_at, synced_from, synced_by + +### Example + +Given local PRD at `C:/my-hq/projects/my-project/prd.json` and target repo at `C:/repos/my-project`: + +```bash +# Ensure directory exists +mkdir -p C:/repos/my-project/.hq + +# The prd.json written to C:/repos/my-project/.hq/prd.json: +{ + "project": "my-project", + "goal": "...", + "features": [...], + "metadata": {...}, + "sync_metadata": { + "synced_at": "2026-01-27T15:30:00Z", + "synced_from": "C:/my-hq/projects/my-project/prd.json", + "synced_by": "pure-ralph" + } +} +``` + +### Commit Strategy + +You have two options: + +1. **Combined commit** (recommended): Include the `.hq/prd.json` update in your task commit + ``` + git add {target_repo}/.hq/prd.json + git commit -m "feat(TASK-ID): Brief description" + ``` + +2. **Separate commit**: Commit task first, then sync + ``` + git commit -m "feat(TASK-ID): Brief description" + git add {target_repo}/.hq/prd.json + git commit -m "sync: update distributed tracking" + ``` + +The combined approach is preferred as it keeps task completion and status in atomic sync. + +--- + +## Distributed Tracking - Pull from Repo + +Before starting work, check if the target repo has distributed tracking data that may contain updates from other contributors. + +### pull_from_repo Function + +Execute this at the start of each Pure Ralph session (after branch verification, before task selection). + +Since the prompt now reads directly from .hq/prd.json, the pull is about refreshing from git to get other contributors' changes: + +``` +PULL_FROM_REPO: + 1. Pull latest from remote (if on a tracking branch): + git pull --rebase origin feature/{project-name} 2>/dev/null || true + + 2. Check if .hq/prd.json exists: + if [ ! -f ".hq/prd.json" ]; then + echo "No .hq/prd.json found - will be created" + # Script handles initial setup + fi + + 3. If .hq/prd.json exists, read and validate: + - Check for merge conflicts (<<<<<<< markers) + - Verify JSON is valid + - Report current task progress + + 4. Report current state: + CURRENT STATE: + - Total tasks: X + - Completed: Y + - Remaining: Z +``` + +### Diff Detection Logic + +Compare tasks by ID and report differences: + +``` +For each task in LOCAL PRD: + If task.id NOT in REPO → "Task {id} exists locally but not in repo" + If task.passes != repo_task.passes → "Task {id} status differs (local: {x}, repo: {y})" + If task.notes != repo_task.notes → "Task {id} notes differ" + +For each task in REPO PRD: + If task.id NOT in LOCAL → "Task {id} exists in repo but not locally" +``` + +### What Gets Compared + +| Field | Compare? | Why | +|-------|----------|-----| +| `id` | Key | Used to match tasks across PRDs | +| `passes` | Yes | Status changes indicate work done | +| `notes` | Yes | Implementation details may have changed | +| `acceptance_criteria` | Yes | Scope changes need attention | +| `title` | No | Minor wording changes don't matter | +| `description` | No | Details don't affect task matching | +| `dependsOn` | Yes | Dependency changes affect execution order | + +### Example Output + +``` +PULL_FROM_REPO: Checking {target_repo}/.hq/prd.json... + +DIFF SUMMARY: +┌─────────────────────────────────────────────────────────┐ +│ Tasks with different status: │ +│ - US-003: local=false, repo=true │ +│ - US-005: local=false, repo=true │ +│ │ +│ Tasks with updated notes: │ +│ - US-003: "Worker: backend-dev. Implemented..." │ +│ │ +│ Tasks added in repo (not in local): │ +│ - US-010: "Add retry logic for sync failures" │ +└─────────────────────────────────────────────────────────┘ + +WARNING: Local and repo PRDs differ. +- 2 tasks completed in repo but not locally +- 1 new task exists in repo + +Recommendation: Run conflict resolution before continuing. +``` + +### Important: No Auto-Overwrite + +The pull_from_repo function: +- **READS** the repo PRD +- **COMPARES** with local PRD +- **REPORTS** differences +- **DOES NOT** automatically overwrite either file + +This is intentional - automatic overwrites could: +- Lose local work in progress +- Create confusing state if tasks were worked on in parallel +- Make debugging harder when things go wrong + +Conflict resolution (below) handles the merge decision. + +--- + +## Distributed Tracking - Conflict Resolution + +When local and repo PRDs differ, use this process to resolve conflicts and merge changes. + +### When to Use + +Run conflict resolution when: +- `pull_from_repo` reports differences +- You're about to start work and suspect others have been working on the same project +- Manual sync via `/sync-tasks` detects conflicts + +### Task-Level Diff Display + +For each task with differences, show a detailed comparison: + +``` +CONFLICT: Task {task_id} - {title} +┌─────────────────────────────────────────────────────────────┐ +│ Field │ Local │ Repo │ +├────────────────┼──────────────────────┼─────────────────────┤ +│ passes │ false │ true │ +│ notes │ (empty) │ "Worker: backend..."│ +│ updated_at │ 2026-01-26T10:00:00Z │ 2026-01-27T14:30:00Z│ +└─────────────────────────────────────────────────────────────┘ + +Repo is newer (2026-01-27T14:30:00Z vs 2026-01-26T10:00:00Z) +Recommendation: Accept repo version +``` + +### Merge Strategy: Per-Task, Newer Wins + +The merge strategy operates at the **task level**, not the PRD level: + +``` +MERGE_STRATEGY: + For each task in either PRD: + 1. If task exists only in LOCAL → keep in merged result + 2. If task exists only in REPO → add to merged result + 3. If task exists in BOTH with differences: + a. Compare updated_at timestamps + b. If repo.updated_at > local.updated_at → use repo version + c. If local.updated_at > repo.updated_at → use local version + d. If timestamps equal or missing → prompt user +``` + +### updated_at Requirement + +For conflict resolution to work automatically, tasks should include `updated_at`: + +```json +{ + "id": "US-003", + "title": "Add pull-from-repo function", + "passes": true, + "notes": "Worker: backend-dev...", + "updated_at": "2026-01-27T14:30:00Z" +} +``` + +**Important:** When updating a task's `passes` or `notes`, always update `updated_at` to current timestamp. + +If `updated_at` is missing from both versions, fall back to manual resolution. + +### User Confirmation Prompt + +Before applying any merge, prompt the user: + +``` +MERGE PREVIEW: +┌─────────────────────────────────────────────────────────────┐ +│ Changes to apply: │ +│ │ +│ ACCEPT FROM REPO (newer): │ +│ - US-003: passes false → true │ +│ - US-003: notes updated with implementation details │ +│ │ +│ KEEP LOCAL (newer): │ +│ - US-007: acceptance_criteria refined │ +│ │ +│ ADD FROM REPO (new tasks): │ +│ - US-010: "Add retry logic for sync failures" │ +│ │ +│ REQUIRES MANUAL DECISION (no timestamp or equal): │ +│ - US-005: Both modified, cannot auto-resolve │ +└─────────────────────────────────────────────────────────────┘ + +Proceed with merge? [y/n/manual] +- y: Apply automatic resolution for timestamped tasks, skip manual ones +- n: Cancel merge, keep local unchanged +- manual: Review each conflict individually +``` + +### Manual Resolution (Interactive) + +When user selects `manual` or for tasks without timestamps: + +``` +CONFLICT: US-005 - Add duplicate work detection +No clear winner (timestamps missing or equal) + +LOCAL version: + passes: false + notes: "Started implementation, WIP" + +REPO version: + passes: false + notes: "Blocked on US-001 clarification" + +Choose: + [L] Use LOCAL version + [R] Use REPO version + [M] Merge manually (edit notes) + [S] Skip (leave in conflict state) + +Selection: _ +``` + +### Writing Merged Result + +After user confirms, write to BOTH locations: + +``` +APPLY_MERGE: + 1. Construct merged PRD: + - Start with current .hq/prd.json as base + - Apply resolved changes per task + - Update sync_metadata.merged_at = now() + - Add sync_metadata.merge_source = "conflict_resolution" + + 2. Write merged result to .hq/prd.json + + 3. Stage for commit: + git add .hq/prd.json + + 4. Report success: + "Merge complete. Written to .hq/prd.json" +``` + +### Example Merge Flow + +``` +$ Starting Pure Ralph session... + +PULL_FROM_REPO: Checking C:/my-repo/.hq/prd.json... +Found differences - initiating conflict resolution. + +CONFLICT: US-003 - Add pull-from-repo function +┌─────────────────────────────────────────────────────────────┐ +│ Field │ Local │ Repo │ +├────────────────┼──────────────────────┼─────────────────────┤ +│ passes │ false │ true │ +│ notes │ (empty) │ "Worker: backend..."│ +│ updated_at │ - │ 2026-01-27T14:30:00Z│ +└─────────────────────────────────────────────────────────────┘ +Repo has timestamp, local doesn't → Accept repo version + +MERGE PREVIEW: + ACCEPT FROM REPO: US-003 (completed by another contributor) + +Proceed with merge? [y/n/manual]: y + +Applying merge... +✓ Updated local PRD: C:/my-hq/projects/my-project/prd.json +✓ Updated repo PRD: C:/my-repo/.hq/prd.json + +Merge complete. Continuing with task selection... +``` + +### Conflict States + +Tasks can be in these conflict states: + +| State | Meaning | Resolution | +|-------|---------|------------| +| `no_conflict` | Local and repo match | No action needed | +| `repo_newer` | Repo has newer updated_at | Auto-accept repo | +| `local_newer` | Local has newer updated_at | Auto-keep local | +| `manual_required` | No timestamps or equal | User decides | +| `new_in_repo` | Task only in repo | Auto-add to local | +| `new_in_local` | Task only in local | Auto-add to repo | + +--- + +## Distributed Tracking - Task Claims + +Before starting any task, check if it's claimed by another contributor. This prevents duplicate work in distributed teams. + +### Claim Lifecycle + +1. **Before task:** Check if claimed by someone else +2. **If available:** Claim the task +3. **Work on task:** Implement and complete +4. **After task:** Release the claim + +### check_claim Function + +Execute before starting any task: + +``` +CHECK_CLAIM: + 1. Read claims file (create if missing): + claims_path = {target_repo}/.hq/claims.json + if [ ! -f "$claims_path" ]; then + echo '{"claims": [], "updated_at": null}' > "$claims_path" + fi + + 2. Parse claims and find matching task: + For claim in claims.claims: + if claim.task_id == selected_task.id: + Check if expired (claim.expires_at < now) + If NOT expired → CLAIMED by someone else + If expired → Available (stale claim) + + 3. If claimed by someone else (not expired): + ┌─────────────────────────────────────────────────────────────┐ + │ WARNING: Task {task_id} is claimed │ + │ │ + │ Claimed by: {claimed_by} │ + │ Claimed at: {claimed_at} │ + │ Expires at: {expires_at} │ + │ │ + │ Options: │ + │ [S] Skip - pick a different task │ + │ [W] Wait - task may be released soon │ + │ [O] Override - claim anyway (use with caution) │ + └─────────────────────────────────────────────────────────────┘ + + 4. If available (no claim or expired): + Proceed to claim_task +``` + +### claim_task Function + +Claim a task before starting work: + +``` +CLAIM_TASK: + 1. Generate claim record: + { + "task_id": "{selected_task.id}", + "claimed_by": "{identifier}", // e.g., "pure-ralph-{hostname}" or username + "claimed_at": "{ISO 8601 now}", + "expires_at": "{ISO 8601 now + 24 hours}", + "notes": "Working on: {task_title}" + } + + 2. Read current claims.json + + 3. Remove any existing claim for this task_id (expired or otherwise) + + 4. Add new claim to claims array + + 5. Update claims.updated_at to now + + 6. Write updated claims.json to {target_repo}/.hq/claims.json + + 7. Commit: + git add {target_repo}/.hq/claims.json + git commit -m "claim: {task_id} by {claimed_by}" +``` + +### Claim Expiration + +Claims expire after **24 hours** by default. This prevents indefinite blocking if: +- A contributor abandons work +- A session crashes without releasing +- Someone forgets to release + +``` +EXPIRATION_RULES: + - Default: 24 hours from claimed_at + - Expired claims: Automatically considered "available" + - Override: Can claim even if not expired (with warning) + - Refresh: Re-claiming extends expiration +``` + +To calculate expires_at: +``` +expires_at = claimed_at + 24 hours +// Example: claimed_at 2026-01-27T10:00:00Z → expires_at 2026-01-28T10:00:00Z +``` + +### release_claim Function + +Release a claim after task completion: + +``` +RELEASE_CLAIM: + 1. Read claims.json from {target_repo}/.hq/claims.json + + 2. Remove claim matching task_id: + claims.claims = claims.claims.filter(c => c.task_id != completed_task.id) + + 3. Update claims.updated_at to now + + 4. Write updated claims.json + + 5. Include in task completion commit: + git add {target_repo}/.hq/claims.json + // Include with feat(TASK-ID) commit, no separate commit needed +``` + +### Claim Identifier + +The `claimed_by` field should uniquely identify the claimer: + +| Context | Format | Example | +|---------|--------|---------| +| Pure Ralph | `pure-ralph-{hostname}` | `pure-ralph-DESKTOP-ABC` | +| User session | `{username}` | `stefan` | +| CI/CD | `ci-{pipeline-id}` | `ci-12345` | +| Unknown | `anonymous-{timestamp}` | `anonymous-1706360000` | + +### Warning Display + +When a task is claimed, show a prominent warning: + +``` +╔═══════════════════════════════════════════════════════════════╗ +║ ⚠️ TASK CLAIMED ║ +╠═══════════════════════════════════════════════════════════════╣ +║ ║ +║ Task: US-006 - Add task claim mechanism ║ +║ Claimed by: pure-ralph-WORKSTATION ║ +║ Since: 2026-01-27T14:30:00Z (2 hours ago) ║ +║ Expires: 2026-01-28T14:30:00Z (in 22 hours) ║ +║ ║ +║ This task is being worked on by another contributor. ║ +║ Claiming it may result in duplicate work. ║ +║ ║ +╚═══════════════════════════════════════════════════════════════╝ + +Select action: [S]kip / [W]ait / [O]verride: _ +``` + +### Integration with Task Selection + +The claim check integrates into the task selection flow: + +``` +TASK_SELECTION_WITH_CLAIMS: + 1. Find eligible tasks (passes=false, dependencies met) + 2. For each eligible task: + a. check_claim(task.id) + b. If claimed by others (not expired): mark as "claimed" + c. If available: mark as "available" + 3. Prefer "available" tasks over "claimed" tasks + 4. If only claimed tasks remain: show warning, offer options + 5. On task selection: claim_task(selected_task.id) + 6. After task completion: release_claim(task.id) +``` + +### Example claims.json + +```json +{ + "claims": [ + { + "task_id": "US-006", + "claimed_by": "pure-ralph-WORKSTATION", + "claimed_at": "2026-01-27T14:30:00Z", + "expires_at": "2026-01-28T14:30:00Z", + "notes": "Working on: Add task claim mechanism" + }, + { + "task_id": "US-010", + "claimed_by": "stefan", + "claimed_at": "2026-01-27T12:00:00Z", + "expires_at": "2026-01-28T12:00:00Z", + "notes": "Manual claim for prompt templating work" + } + ], + "updated_at": "2026-01-27T14:30:00Z" +} +``` + +--- + +## Distributed Tracking - Integrated Workflow + +This section ties together all distributed tracking functions into the Pure Ralph loop lifecycle. Follow this workflow every session. + +### Session Lifecycle with Distributed Tracking + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ PURE RALPH SESSION │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ 1. BRANCH VERIFICATION │ +│ └── Ensure on feature/{project-name} │ +│ │ +│ 2. DISTRIBUTED SYNC (start of session) ← NEW │ +│ ├── pull_from_repo() │ +│ ├── If conflicts → run conflict resolution │ +│ └── Continue with synced PRD │ +│ │ +│ 3. TASK SELECTION │ +│ ├── Find eligible tasks (passes=false, deps met) │ +│ ├── check_claim() for each candidate ← NEW │ +│ ├── Prefer unclaimed tasks │ +│ └── claim_task() on selected task ← NEW │ +│ │ +│ 4. IMPLEMENTATION │ +│ └── Complete the task │ +│ │ +│ 5. PRD UPDATE │ +│ ├── Set passes: true, add notes │ +│ └── Add updated_at timestamp ← NEW │ +│ │ +│ 6. POST-TASK SYNC ← NEW │ +│ ├── push_to_repo() │ +│ └── release_claim() │ +│ │ +│ 7. COMMIT │ +│ └── Include .hq/ files in commit │ +│ │ +│ 8. LOOP CHECK │ +│ ├── If more tasks → EXIT (new session) │ +│ └── If all complete → push final, create PR │ +│ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +### On Loop Start (After Branch Verification) + +Execute before reading the PRD: + +``` +LOOP_START: + 1. pull_from_repo() + - Pull latest from remote if tracking branch exists + - Check if .hq/prd.json exists (script creates if not) + - Validate JSON and check for merge conflicts + + 2. If merge conflicts in .hq/prd.json: + - Resolve git conflicts + - Ensure valid JSON + + 3. Continue with .hq/prd.json +``` + +### Before Each Task (During Task Selection) + +Execute after identifying eligible tasks, before selecting one: + +``` +BEFORE_TASK: + 1. List all eligible tasks (passes=false, dependencies met) + + 2. For each eligible task: + check_claim(task.id) + - If claimed (not expired): mark as "claimed by {x}" + - If available (no claim or expired): mark as "available" + + 3. Display task list with claim status: + ┌──────────────────────────────────────────────────────────┐ + │ Eligible Tasks: │ + │ US-007 [AVAILABLE] - Integrate with pure-ralph loop │ + │ US-008 [CLAIMED by stefan, 2h ago] - Add /sync-tasks │ + │ US-010 [AVAILABLE] - Add prompt templating │ + └──────────────────────────────────────────────────────────┘ + + 4. Select an AVAILABLE task (prefer available over claimed) + + 5. claim_task(selected_task.id) + - Write claim to {target_repo}/.hq/claims.json + - Claim expires in 24 hours +``` + +### After Each Task (Before Commit) + +Execute after updating PRD, before committing: + +``` +AFTER_TASK: + 1. Ensure PRD has updated_at on the completed task: + { + "id": "{task_id}", + "passes": true, + "notes": "...", + "updated_at": "{ISO 8601 now}" ← Required for conflict resolution + } + + 2. push_to_repo() + - Write updated PRD to {target_repo}/.hq/prd.json + - Include sync_metadata + + 3. release_claim(task.id) + - Remove claim from {target_repo}/.hq/claims.json + + 4. Stage distributed tracking files with task files: + git add {task_files} {target_repo}/.hq/prd.json {target_repo}/.hq/claims.json +``` + +### On Loop Complete (All Tasks Done) + +Execute when all tasks have passes=true: + +``` +LOOP_COMPLETE: + 1. Final push_to_repo() + - Ensure .hq/prd.json reflects all completed tasks + + 2. Verify no orphaned claims: + - Read claims.json + - Remove any claims for this session (should already be released) + - Commit if changes made + + 3. Create PR (see PR Creation section) + - Include .hq/ directory in PR + - PR enables distributed visibility of completed work +``` + +### Error Recovery + +If a session crashes or exits unexpectedly: + +| Situation | Recovery | +|-----------|----------| +| Task claimed but not completed | Claim expires in 24h; next session can claim | +| PRD updated locally but not pushed | Next session will push; conflict resolution handles | +| Claim not released after completion | Claim expires automatically; no manual action needed | +| Merge conflict on .hq/claims.json | Use git merge strategy; newer timestamps win | + +### Quick Reference: Session Steps + +Updated session workflow with distributed tracking: + +| Step | Action | Distributed Tracking | +|------|--------|---------------------| +| 1 | Verify branch | - | +| 2 | **Sync** | pull_from_repo, resolve conflicts | +| 3 | Read PRD | - | +| 4 | Pick task | check_claim for each, prefer unclaimed | +| 5 | **Claim** | claim_task before starting | +| 6 | Implement | - | +| 7 | Update PRD | Include updated_at | +| 8 | **Push** | push_to_repo | +| 9 | **Release** | release_claim | +| 10 | Commit | Include .hq/ files | +| 11 | Check complete | If all done: final push, create PR | + +--- + ## Self-Improvement This prompt can evolve. If you learn something valuable: @@ -441,10 +1127,6 @@ Only add patterns that: **Pattern:** Check `git branch --show-current` immediately before committing; abort if on main/master **Why:** Hard block prevents accidental commits to main; recovery after commit is harder than prevention -### [Conflict] Stale Lock Detection -**Pattern:** If lock file exists but PID is not running, remove the stale lock and continue -**Why:** Stale locks from crashed sessions shouldn't block future execution; checking process status distinguishes active vs stale locks - --- ## PR Creation (When All Tasks Complete)