name: File Protocol description: This skill should be used when implementing "file-based communication", "agent communication via files", "JSON state management", "file locking", "race condition handling", "atomic writes", or building systems where multiple agents coordinate through shared files. Provides comprehensive guidance for file-based inter-agent communication protocols. version: 0.1.0
File Protocol Skill
Overview
This skill provides guidance for implementing file-based communication protocols that enable multiple agents to coordinate through structured file reads and writes. This approach is particularly valuable for multi-agent systems where agents need to share state, signal events, and coordinate actions without direct API communication.
Core Concepts
File-Based Communication
File-based communication uses the filesystem as a message-passing and state-sharing medium:
Advantages:
- Simple and transparent (can inspect files manually)
- Language/process agnostic
- Built-in persistence (files survive process crashes)
- Easy debugging and logging
- No network configuration required
Challenges:
- Race conditions (simultaneous reads/writes)
- File locking coordination
- Atomicity concerns
- Performance (disk I/O overhead)
Best for: Multi-agent coordination, game state management, asynchronous workflows
Communication Patterns
Signal Files: Trigger-based coordination
- File presence or modification signals an event
- Example:
turn-signal.jsontriggers player agent spawn
State Files: Shared state management
- Central authority writes, multiple readers consume
- Example:
game-state.jsonmaintains authoritative state
Action Files: Request/response communication
- Agents write action requests, coordinator processes and responds
- Example: Player writes
player-actions/player-1.json, gamemaster reads and validates
Log Files: Append-only history
- Immutable record of events for debugging and analysis
- Example:
game-log.jsonrecords all moves
File Structure Design
Directory Organization
Organize files by purpose and access pattern:
games/<game-name>/
├── RULES.md # Static configuration (read-only)
├── state/ # Active game state (read-write)
│ ├── game-state.json # Authoritative state (GM writes, all read)
│ ├── turn-signal.json # Turn notifications (GM writes, hooks read)
│ ├── player-actions/ # Player decisions (players write, GM reads)
│ │ ├── player-1.json
│ │ └── player-2.json
│ └── .locks/ # Lock files for coordination
│ └── game-state.lock
├── logs/ # Completed games (append-only)
│ └── game-2024-01-27.json
└── traces/ # Detailed debugging (append-only)
└── game-2024-01-27.md
File Naming Conventions
Use consistent, descriptive names:
State files: Describe the content
game-state.json(notstate.json)turn-signal.json(notsignal.json)player-1-hand.json(notp1.json)
Action files: Include actor identifier
player-actions/player-1.jsonplayer-actions/player-2.json
Lock files: Match the protected resource
.locks/game-state.lock.locks/turn-signal.lock
Log files: Include timestamp
logs/game-2024-01-27-14-30-00.jsontraces/game-2024-01-27-14-30-00.md
JSON Schema Design
Principles
Include metadata: Every file should have context
{
"fileType": "game-state",
"version": "1.0",
"timestamp": "2024-01-27T14:30:00Z",
"gameId": "uno-game-42",
"data": { ... }
}
Use explicit types: Avoid ambiguous values
// Good
{"action": "play", "card": {"color": "Red", "value": "7"}}
// Bad
{"action": "p", "c": "R7"}
Include validation: Add fields for integrity checking
{
"turnNumber": 42,
"previousTurnHash": "abc123...",
"playerId": "player-1",
"action": { ... }
}
State File Schema
Authoritative game state maintained by gamemaster:
{
"fileType": "game-state",
"version": "1.0",
"timestamp": "2024-01-27T14:30:00Z",
"game": "UNO",
"gameId": "uno-game-42",
"turnNumber": 12,
"gameActive": true,
"players": [
{
"id": "player-1",
"cardCount": 5,
"score": 0,
"isActive": true
}
],
"currentPlayer": "player-1",
"direction": 1,
"deck": {
"remaining": 42,
"shuffled": true
},
"discardPile": [
{"color": "Red", "value": "7"}
]
}
Turn Signal Schema
Signals which player should act:
{
"fileType": "turn-signal",
"version": "1.0",
"timestamp": "2024-01-27T14:30:05Z",
"gameId": "uno-game-42",
"turnNumber": 12,
"currentPlayer": "player-1",
"availableActions": ["play", "draw"],
"visibleState": {
"discardTop": {"color": "Red", "value": "7"},
"opponentCardCounts": {
"player-2": 3,
"player-3": 6,
"player-4": 4
}
}
}
Action File Schema
Player decision output:
{
"fileType": "player-action",
"version": "1.0",
"timestamp": "2024-01-27T14:30:10Z",
"gameId": "uno-game-42",
"playerId": "player-1",
"turnNumber": 12,
"action": "play",
"card": {
"color": "Blue",
"value": "7"
},
"reasoning": "Changing to Blue to keep Red 7 as option",
"confidence": 0.85
}
See references/schema-reference.md for complete schema specifications and validation rules.
Atomic Operations
Read-Modify-Write Pattern
When updating state based on previous value:
- Acquire lock
while ! mkdir .locks/game-state.lock 2>/dev/null; do
sleep 0.1
done
- Read current state
const state = JSON.parse(await Read("games/uno/state/game-state.json"));
- Modify state
state.turnNumber += 1;
state.currentPlayer = "player-2";
- Write atomically
// Write to temp file first
await Write("games/uno/state/game-state.json.tmp", JSON.stringify(state, null, 2));
// Atomic rename
await Bash("mv games/uno/state/game-state.json.tmp games/uno/state/game-state.json");
- Release lock
rmdir .locks/game-state.lock
Write-Only Pattern
When writing new file (no read needed):
- Generate unique filename
const filename = `player-actions/player-${playerId}-${Date.now()}.json`;
- Write directly (no lock needed for new files)
await Write(`games/uno/state/${filename}`, JSON.stringify(action, null, 2));
Race Condition Handling
Common Scenarios
Scenario 1: Simultaneous state updates
- Problem: Two agents try to update game-state.json
- Solution: Use lock files, only one agent writes at a time
Scenario 2: Reading stale data
- Problem: Agent reads state before latest update completes
- Solution: Include version numbers or timestamps, retry on conflict
Scenario 3: Lost updates
- Problem: Second write overwrites first write
- Solution: Atomic write pattern with lock acquisition
See references/race-conditions.md for detailed scenarios and solutions.
File Locking Strategies
Directory-Based Locks
Most reliable cross-platform approach:
# Acquire lock (mkdir is atomic)
while ! mkdir .locks/resource.lock 2>/dev/null; do
sleep 0.1
# Timeout after 10 seconds
if [ $SECONDS -gt 10 ]; then
echo "Lock timeout"
exit 1
fi
done
# Critical section
# ... perform locked operations ...
# Release lock
rmdir .locks/resource.lock
PID-Based Locks
Track lock owner for debugging:
# Acquire with PID
mkdir .locks/resource.lock
echo $$ > .locks/resource.lock/owner
# Critical section
# ...
# Release
rm .locks/resource.lock/owner
rmdir .locks/resource.lock
Stale Lock Detection
Handle crashes that leave locks:
# Check lock age
if [ -d .locks/resource.lock ]; then
lock_age=$(($(date +%s) - $(stat -f%m .locks/resource.lock)))
if [ $lock_age -gt 30 ]; then
echo "Removing stale lock"
rmdir .locks/resource.lock
fi
fi
See references/locking-patterns.md for complete implementations.
Hook Integration
File-based communication integrates with hooks for event-driven coordination:
PostToolUse(Write) hook detects file changes:
{
"PostToolUse": [{
"matcher": "Write",
"hooks": [{
"type": "prompt",
"prompt": "A file was written: {{tool_use.parameters.file_path}}. If this is a turn-signal or player-action file in games/*/state/, trigger appropriate agent response."
}]
}]
}
See hook-sync skill for detailed hook implementation.
Best Practices
File Design
✅ DO:
- Use descriptive, consistent naming
- Include metadata in every file
- Use JSON for structured data
- Version your schemas
- Add timestamps for debugging
❌ DON'T:
- Use cryptic abbreviations
- Mix different data types in one file
- Rely on file modification times only
- Change schemas without versioning
- Omit error context
Concurrency
✅ DO:
- Use locks for read-modify-write
- Implement timeout for lock acquisition
- Clean up stale locks
- Write to temp files then rename
- Handle lock contention gracefully
❌ DON'T:
- Skip locks for "fast" operations
- Hold locks longer than necessary
- Ignore lock acquisition failures
- Write directly to final file
- Assume operations are instant
Error Handling
✅ DO:
- Validate JSON schema on read
- Check file existence before reading
- Handle parse errors gracefully
- Log all file operations
- Provide clear error messages
❌ DON'T:
- Assume files always exist
- Skip validation
- Silently fail
- Leave incomplete files
- Ignore I/O errors
Debugging
File Inspection
Monitor file changes in real-time:
# Watch directory for changes
watch -n 0.5 'ls -lt games/uno/state/'
# Tail logs as they're written
tail -f games/uno/logs/game-latest.json
# View file with timestamps
stat -f "%Sm %N" -t "%Y-%m-%d %H:%M:%S" games/uno/state/*.json
Validation Tools
Validate JSON files:
# Check JSON syntax
jq empty games/uno/state/game-state.json
# Validate against schema
ajv validate -s schema.json -d games/uno/state/game-state.json
# Pretty-print for inspection
jq . games/uno/state/game-state.json
Common Issues
File not found: Check paths, ensure directory exists Parse error: Validate JSON syntax, check for partial writes Stale data: Verify atomic write pattern, check timestamps Lock contention: Review lock acquisition logic, add logging Race condition: Add locks, use atomic operations
Additional Resources
Reference Files
For detailed implementation guidance:
references/schema-reference.md- Complete JSON schemas and validationreferences/race-conditions.md- Detailed race condition scenarios and solutionsreferences/locking-patterns.md- Advanced locking implementations
Example Files
Working examples in examples/:
file-operations.sh- Shell scripts for atomic operationsstate-schemas.json- JSON schema definitionsvalidation-example.js- File validation implementation
Integration
This skill works together with:
- game-coordination: Agents that read/write these files
- hook-sync: Hooks that detect file changes and trigger agents
For complete multi-agent file-based coordination, use all three skills together.