|
| 1 | +# Data Safety Guidelines |
| 2 | + |
| 3 | +This document establishes patterns for safely updating the status hub's data files without data loss. |
| 4 | + |
| 5 | +## Core Principle |
| 6 | + |
| 7 | +**Never overwrite, always merge.** When updating a subset of data (e.g., just PRs or just calendar), preserve items from other services. |
| 8 | + |
| 9 | +## Bridge File Updates (`/tmp/status-hub.json`) |
| 10 | + |
| 11 | +The bridge file has three main sections: |
| 12 | +- `timestamp`: Always update on write |
| 13 | +- `background`: Music/service status |
| 14 | +- `foreground`: Array of tracked items (PRs, calendar, Slack, etc.) |
| 15 | + |
| 16 | +### Correct Pattern: Merge Foreground Items |
| 17 | + |
| 18 | +When refreshing one service, preserve items from other services: |
| 19 | + |
| 20 | +```bash |
| 21 | +# Get existing non-PR items before rebuilding PR list |
| 22 | +NON_PR_ITEMS=$(jq -c '[.foreground[] | select(.owner | not)]' "$CONFIG" 2>/dev/null || echo '[]') |
| 23 | + |
| 24 | +# After building PR array, merge with non-PR items |
| 25 | +echo "$pr_array" | jq --argjson nonpr "$NON_PR_ITEMS" '. + $nonpr' |
| 26 | +``` |
| 27 | + |
| 28 | +### Wrong Pattern: Full Replacement |
| 29 | + |
| 30 | +```bash |
| 31 | +# BAD: This loses Slack, calendar, and other items! |
| 32 | +jq '.foreground = $prs' --argjson prs "$PR_ONLY_ARRAY" "$BRIDGE" |
| 33 | +``` |
| 34 | + |
| 35 | +## Config File Updates (`~/.claude/status-config.json`) |
| 36 | + |
| 37 | +### Correct Pattern: Targeted Updates with `|=` |
| 38 | + |
| 39 | +```bash |
| 40 | +# Update specific item by matching criteria |
| 41 | +jq '(.foreground[] | select(.number == 123)) |= . + {lastSeen: {...}}' "$CONFIG" |
| 42 | + |
| 43 | +# Update background without touching foreground |
| 44 | +jq '.background |= {service: "spotify", tabId: 123}' "$CONFIG" |
| 45 | +``` |
| 46 | + |
| 47 | +### Wrong Pattern: Full Object Replacement |
| 48 | + |
| 49 | +```bash |
| 50 | +# BAD: Replaces entire config, losing other sections! |
| 51 | +echo '{"foreground": [...]}' > "$CONFIG" |
| 52 | +``` |
| 53 | + |
| 54 | +## Adding New Items |
| 55 | + |
| 56 | +When adding a new tracked item: |
| 57 | + |
| 58 | +1. Read existing foreground array |
| 59 | +2. Check if item already exists (match by unique key like `owner/repo/number`) |
| 60 | +3. If exists: update in place with `|=` |
| 61 | +4. If new: append with `+= [$new_item]` |
| 62 | + |
| 63 | +```bash |
| 64 | +# Check if PR exists, then add or update |
| 65 | +if jq -e ".foreground[] | select(.owner == \"$owner\" and .repo == \"$repo\" and .number == $number)" "$CONFIG" >/dev/null 2>&1; then |
| 66 | + # Update existing |
| 67 | + jq "(.foreground[] | select(.number == $number)) |= . + {lastSeen: {...}}" "$CONFIG" |
| 68 | +else |
| 69 | + # Append new |
| 70 | + jq ".foreground += [{owner: \"$owner\", repo: \"$repo\", number: $number}]" "$CONFIG" |
| 71 | +fi |
| 72 | +``` |
| 73 | + |
| 74 | +## Checklist for Skill Authors |
| 75 | + |
| 76 | +Before submitting a skill that modifies hub data: |
| 77 | + |
| 78 | +- [ ] Does it preserve items from other services when updating foreground? |
| 79 | +- [ ] Does it use `|=` for targeted updates instead of full replacement? |
| 80 | +- [ ] Does it check for existing items before adding new ones? |
| 81 | +- [ ] Does it preserve background when only updating foreground (and vice versa)? |
| 82 | +- [ ] Does it handle missing files gracefully (create with sensible defaults)? |
| 83 | + |
| 84 | +## Race Condition Prevention |
| 85 | + |
| 86 | +When multiple processes might update the same file: |
| 87 | + |
| 88 | +1. Use atomic writes: write to `.tmp` file first, then `mv` to final location |
| 89 | +2. For daemons: use lockfile with ownership verification (version:PID) |
| 90 | +3. Check file staleness before modifying |
| 91 | + |
| 92 | +```bash |
| 93 | +# Atomic write pattern |
| 94 | +jq '...' "$CONFIG" > "${CONFIG}.tmp" && mv "${CONFIG}.tmp" "$CONFIG" |
| 95 | +``` |
| 96 | + |
| 97 | +## Related Files |
| 98 | + |
| 99 | +- `bin/refresh-prs.sh` - PR-only refresh (preserves non-PR items) |
| 100 | +- `bin/update-bridge.sh` - Bridge writer (preserves background/foreground appropriately) |
| 101 | +- `bin/refresh-daemon.sh` - Background daemon with lockfile management |
| 102 | +- `skills/hub-refresh.md` - Full refresh skill |
0 commit comments