22 KiB
IDEAS.md
Role-Based Sessions
Specialized session types with enforced capabilities via context and sandbox configuration.
Current Implementation
Already possible using existing features:
contextfile defines role behavior- Sandbox config enforces filesystem boundaries
- Scripts create role-specific sessions (see
scripts/DevReview,scripts/Planning)
Example Roles
Developer:
- Context: implements features, stages changes, requests reviews
- Sandbox: read-write in project directory
- Can: modify code, run tests, commit to feature branches
- Cannot: push to main, merge PRs
Reviewer:
- Context: reviews staged changes, provides feedback
- Sandbox: read-only filesystem
- Can: read code, run git diff, analyze changes
- Cannot: modify files, commit, push
Planner:
- Context: analyzes requirements, creates task breakdowns
- Sandbox: read-only filesystem, no git write
- Can: read codebase, write plans to separate files
- Cannot: modify code, commit, push
Tester:
- Context: writes and runs tests
- Sandbox: read-write in test directories only
- Can: create test files, run test commands
- Cannot: modify source code outside test dirs
Implementation
1. Role Templates
Store role definitions in ~/.config/anvillm/roles/:
roles/
├── developer.txt
├── reviewer.txt
├── planner.txt
└── tester.txt
Scripts load templates:
cat ~/.config/anvillm/roles/developer.txt | 9p write agent/$SESSION_ID/context
2. Role-Specific Sandbox Configs
Create per-role sandbox configs in ~/.config/anvillm/sandbox-roles/:
# sandbox-roles/reviewer.yaml
filesystem:
ro:
- "{CWD}"
- "/usr"
- "/lib"
rw: [] # No write access
network:
enabled: false
# sandbox-roles/developer.yaml
filesystem:
ro:
- "/usr"
- "/lib"
rw:
- "{CWD}"
- "{TMPDIR}"
network:
enabled: true
Session creation with role:
echo 'new kiro /project reviewer' | 9p write agent/ctl
# Daemon loads sandbox-roles/reviewer.yaml for this session
3. Role Metadata
Add agent/{session}/role file to 9P filesystem:
agent/<id>/role # "developer" | "reviewer" | "planner" | "tester"
Benefits:
- UI can filter/group by role
- Scripts can discover sessions by role
- Easier than parsing aliases
4. Role Discovery
Find sessions by role:
# List all reviewer sessions
9p read agent/list | while read id alias state pid cwd; do
role=$(9p read agent/$id/role)
[ "$role" = "reviewer" ] && echo "$id $alias"
done
Or use peer-bot skill (already works):
Find the reviewer agent and send them: "Please review staged changes"
Enforcement Layers
Soft enforcement (current):
- Context instructions tell bot what its role is
- Bot follows instructions (usually)
Hard enforcement (with role-specific sandboxes):
- Filesystem boundaries prevent unauthorized writes
- Reviewer literally cannot modify code files
- Planner cannot commit or push
Example Workflow
# Create developer session with role
DEV_ID=$(echo 'new kiro /project developer' | 9p write agent/ctl && ...)
cat ~/.config/anvillm/roles/developer.txt | 9p write agent/$DEV_ID/context
# Create reviewer session with role
REV_ID=$(echo 'new kiro /project reviewer' | 9p write agent/ctl && ...)
cat ~/.config/anvillm/roles/reviewer.txt | 9p write agent/$REV_ID/context
# Developer implements feature
echo "Implement user authentication" | 9p write agent/$DEV_ID/in
# Developer requests review (via peer-bot skill)
# Reviewer provides feedback
# Loop until LGTM
Benefits
- Separation of concerns: Each role has clear responsibilities
- Safety: Sandbox prevents accidental damage (reviewer can't modify code)
- Reusability: Role templates used across projects
- Discoverability: Find sessions by role, not just alias
- Scalability: Spawn multiple workers of same role
Already Working
See scripts/DevReview and scripts/Planning for working examples of role-based multi-agent workflows using context system.
Persistent Work Tracking (Beads)
Git-backed work units that survive orchestrator/bot crashes and preserve workflow state.
Problem
Current workflow: Bot A produces file, instruct Bot B to read it. Works, but if either bot dies or orchestrator crashes, there's no record of:
- What the overall goal was
- What's been completed
- What still needs to be done
- Dependencies between tasks
Beads Concept
Inspired by Gas Town's Beads system. Each work unit is a persistent file (TOML/JSON) storing:
[bead]
id = "research-arch-001"
title = "Research codebase architecture"
description = "Analyze project structure and document key components"
status = "completed" # pending | in_progress | completed | failed
assigned_to = "research-bot-a3f2b9d1"
result_file = "/tmp/architecture-overview.md"
created_at = 1739587200
updated_at = 1739590800
depends_on = [] # Array of bead IDs that must complete first
9P Integration
Expose beads via filesystem:
agent/
├── beads/
│ ├── ctl # "new <title> <role> <description>" creates bead
│ ├── list # All beads with status
│ └── <bead-id>/
│ ├── status # pending | in_progress | completed | failed
│ ├── title # Human-readable name
│ ├── description # What needs to be done
│ ├── role # Which type of bot should handle this
│ ├── assigned_to # Session ID of bot working on it
│ ├── result # Output/findings (file path or inline text)
│ ├── depends_on # Newline-separated list of bead IDs
│ └── ctl # "claim <session-id>", "complete", "fail"
Workflow
- Create beads: Orchestrator or "mayor" bot breaks down goal into beads
- Bots claim beads: Worker bots read
beads/list, claim pending beads matching their role - Execute: Bot works on task, writes result
- Complete: Bot marks bead as completed, writes result file path
- Crash recovery: If bot dies, bead remains
in_progress. Orchestrator can reassign or restart
Example: Research → Development
# Create research bead
echo 'new "Research auth system" researcher "Analyze existing auth code"' | 9p write agent/beads/ctl
# Research bot claims it
echo 'claim research-bot-123' | 9p write agent/beads/research-auth-001/ctl
# Bot completes work
echo '/tmp/auth-analysis.md' | 9p write agent/beads/research-auth-001/result
echo 'complete' | 9p write agent/beads/research-auth-001/ctl
# Create dependent dev bead
echo 'new "Implement OAuth" developer "Add OAuth based on research" research-auth-001' | 9p write agent/beads/ctl
# Dev bot waits for dependency, then claims
9p read agent/beads/research-auth-001/status # → "completed"
echo 'claim dev-bot-456' | 9p write agent/beads/impl-oauth-002/ctl
Benefits
- Crash resilient: Work state persists on disk
- Resumable: New orchestrator can read beads and continue
- Auditable: Git-backed beads provide full history
- Dependency tracking: Bots wait for prerequisites automatically
- Role-based: Specialized bots pick up beads matching their role
Git Integration
Commit beads after state changes:
git add agent/beads/
git commit -m "Completed: Research auth system"
Full audit trail of what was done, when, and by whom.
Philosophy: Precision Over Throughput
Unlike Gas Town's "spawn 30 workers and see what sticks" approach, beads in AnviLLM enable:
- Deliberate sequencing: Dependencies ensure work happens in correct order
- Quality gates: Beads can require approval before proceeding
- Traceability: Every decision and result is recorded
- Human oversight: Orchestrator (human or bot) reviews bead results before creating dependent beads
Approval Gates
Sessions pause for human or coordinator approval before proceeding.
Use Case
Bot completes a task but shouldn't continue until work is reviewed:
- Developer bot implements feature
- Bot writes summary to
out, transitions toawait_approval - Human/reviewer reads summary, examines changes
- Human sends approval:
echo 'approve' | 9p write agent/{session}/ctl - Bot continues with next step
States
Add new state to session lifecycle:
idle→running→await_approval→idle(if approved)idle→running→await_approval→stopped(if rejected)
9P Interface
agent/<id>/approval # Read: current approval status
# Write: "approve" or "reject <reason>"
Workflow Example
# Developer bot context includes:
# "After implementing, write summary to out and request approval"
# Bot finishes work
echo "Implemented OAuth login. Added 3 files, 200 LOC. All tests pass." | 9p write agent/dev-123/out
echo 'request_approval' | 9p write agent/dev-123/ctl
# State transitions to await_approval
9p read agent/dev-123/state # → "await_approval"
# Human reviews
9p read agent/dev-123/out
git diff
# Approve
echo 'approve' | 9p write agent/dev-123/approval
# Bot continues (or human sends next prompt)
Integration with Beads
Beads can require approval before marking complete:
[bead]
requires_approval = true
approved_by = "" # Session ID or "human"
Bot completes bead but status stays pending_approval until approved.
Philosophy: Human in the Loop
Approval gates ensure precision:
- Critical changes reviewed before proceeding
- Prevents cascading errors from bad decisions
- Human maintains control over workflow direction
- Bot can work autonomously within approved boundaries
Structured Messaging (Mailboxes)
Typed message passing between sessions via inbox/outbox files.
Problem
Current approach: bots write raw text to each other's in files. Works, but:
- No message history (overwritten on next write)
- No message types (can't distinguish review request from question)
- No metadata (who sent it, when, in response to what)
Mailbox System
agent/<id>/inbox # Read: pending messages (JSON array)
agent/<id>/outbox # Write: send message (JSON object)
agent/<id>/mail/ # Message archive
├── received/
│ └── <msg-id>.json
└── sent/
└── <msg-id>.json
Message Format
{
"id": "msg-a3f2b9d1",
"from": "dev-bot-123",
"to": "reviewer-bot-456",
"type": "REVIEW_REQUEST",
"timestamp": 1739587200,
"subject": "Please review OAuth implementation",
"body": "Implemented OAuth login. Changes staged. Ready for review.",
"metadata": {
"bead_id": "impl-oauth-002",
"files_changed": 3,
"lines_added": 200
}
}
Message Types
REVIEW_REQUEST: Developer asks for code reviewREVIEW_RESPONSE: Reviewer provides feedbackQUESTION: Bot asks peer for informationANSWER: Response to questionAPPROVAL_REQUEST: Bot asks human for approvalAPPROVAL_RESPONSE: Human approves or rejectsSTATUS_UPDATE: Bot reports progressERROR_REPORT: Bot encountered problem
Sending Messages
# Bot writes to outbox
cat <<EOF | 9p write agent/dev-123/outbox
{
"to": "reviewer-bot-456",
"type": "REVIEW_REQUEST",
"subject": "Please review OAuth implementation",
"body": "Changes staged and ready for review."
}
EOF
# Daemon delivers to recipient's inbox
# Recipient reads inbox
9p read agent/reviewer-bot-456/inbox
Reading Messages
# Bot reads inbox (returns JSON array)
9p read agent/reviewer-bot-456/inbox
# Daemon marks messages as read, moves to mail/received/
Benefits
- History: All messages archived, not overwritten
- Typed: Bots can handle different message types appropriately
- Metadata: Rich context about what message relates to
- Traceable: Full communication audit trail
- Structured: Easier to parse than free-form text
Complement, Not Replace
Mailboxes complement existing in file:
in: Direct prompts from human or orchestratorinbox: Structured messages from peer bots- Bot checks both, handles appropriately
Philosophy: Precision Through Structure
Structured messages enable:
- Clear intent: Message type indicates expected response
- Context preservation: Metadata links messages to beads/tasks
- Audit trail: Full record of inter-bot communication
- Error recovery: Can replay message sequence after crash
Convoy (Grouped Work)
Collection of related beads working toward a common goal.
Concept
Convoy = group of beads + shared context + coordinator
[convoy]
id = "auth-system-001"
title = "Implement authentication system"
goal = "Add OAuth and session management to application"
status = "in_progress" # planning | in_progress | completed | failed
coordinator = "planner-bot-789"
created_at = 1739587200
updated_at = 1739590800
beads = [
"research-auth-001",
"impl-oauth-002",
"write-tests-003",
"review-security-004"
]
9P Integration
agent/
├── convoys/
│ ├── ctl # "new <title> <goal>" creates convoy
│ ├── list # All convoys with status
│ └── <convoy-id>/
│ ├── title
│ ├── goal
│ ├── status
│ ├── coordinator # Session ID managing this convoy
│ ├── beads # Newline-separated bead IDs
│ ├── context # Shared context for all beads in convoy
│ └── ctl # "add_bead <id>", "complete", "abandon"
Workflow
- Create convoy: Planner bot or human defines high-level goal
- Break down: Coordinator creates beads for convoy
- Execute: Worker bots claim and complete beads
- Coordinate: Coordinator monitors progress, creates new beads as needed
- Complete: All beads done, convoy marked complete
Example
# Create convoy
echo 'new "Implement auth system" "Add OAuth and session management"' | 9p write agent/convoys/ctl
# Coordinator creates beads
echo 'new "Research auth" researcher "Analyze requirements"' | 9p write agent/beads/ctl
echo 'add_bead research-auth-001' | 9p write agent/convoys/auth-system-001/ctl
echo 'new "Implement OAuth" developer "Add OAuth based on research" research-auth-001' | 9p write agent/beads/ctl
echo 'add_bead impl-oauth-002' | 9p write agent/convoys/auth-system-001/ctl
# Workers execute beads
# Coordinator monitors, adds more beads if needed
# Convoy completes when all beads done
Shared Context
All beads in convoy inherit convoy context:
When working on beads in this convoy, remember:
- Target: web application with React frontend
- Security: OWASP top 10 compliance required
- Timeline: 2 weeks
- Constraints: Must integrate with existing user database
Benefits
- Cohesion: Related work grouped together
- Context sharing: All workers understand overall goal
- Progress tracking: See convoy completion percentage
- Coordination: Coordinator adjusts plan based on results
- Resumability: Convoy persists across crashes
Philosophy: Deliberate Orchestration
Convoys enable precision through:
- Explicit goals: Clear definition of what success looks like
- Adaptive planning: Coordinator adjusts based on results
- Shared understanding: All workers have convoy context
- Traceable decisions: Why each bead was created
Unlike Gas Town's "spawn workers and hope", convoys are deliberate, coordinated, and traceable.
Conductor Bot
A meta-bot that orchestrates workflows by watching status and dispatching prompts.
Role
- Monitors status files of worker bots
- Issues coordination prompts when bots enter
await - Defines workflow through prompt injection, not hardcoded logic
Example: Adversarial Review Cycle
Conductor's initial prompt:
You are coordinating a code review cycle between 5 reviewer bots and 1 dev bot.
Workflow:
1. Tell reviewers: "Review the code in dev/out, write feedback to your out"
2. Wait for all 5 reviewers to enter await
3. Tell dev: "Read reviewer1/out through reviewer5/out, apply feedback, write updated diff to out"
4. Wait for dev to enter await
5. Repeat from step 1 until reviewers report no issues
Monitor status files. Only proceed when all expected bots are in await.
Conductor reads status, sends prompts to in files, watches for await. The workflow is entirely prompt-driven.
Benefits
- Workflows defined in natural language, not code
- Easy to modify - just change conductor's prompt
- Conductor can be swapped, forked, or layered (conductor of conductors)
- Worker bots stay simple - they just do tasks and write to
out
Conductor as Bot vs External Process
Could be:
- Another LLM bot (flexible, can adapt, but slower and uses tokens)
- A simple script that polls status and sends canned prompts (fast, deterministic)
- Hybrid: script handles timing, bot handles decisions when workflow branches
9p-Native Conductor
9p makes coordination trivial. Conductor just reads/writes files - no IPC, no sockets, no message queues. The filesystem is the coordination API.
Pseudocode:
while true:
for bot in bots:
status = read(bot/status)
if status == "await" and bot in pending:
next = workflow.next(bot)
write(next.bot/in, next.prompt)
pending.remove(bot)
pending.add(next.bot)
sleep(poll_interval)
Could be 50 lines of rc, Python, or Go. Workflow definitions could be:
- Hardcoded in the conductor
- Loaded from a config file
- Defined in a
workflowfile in 9p itself (conductor reads its own instructions from the filesystem)
Ephemeral Sessions
Task-oriented sessions that perform a single action then stop automatically.
Lifecycle
idle → running → stopped
No persistent interaction. Bot receives prompt, executes, writes result to out, transitions to stopped.
Design Questions
- Restartable? Should
stoppedephemeral sessions supportrestart, or onlykill? If restartable, they become regular sessions. If not, simpler lifecycle but less flexible. - Non-interactive? Could enforce no tmux attachment, or allow attachment for debugging but expect no human input.
- Kill vs Stop? Maybe ephemeral sessions go straight to
exitedinstead ofstopped. Or usestoppedbut auto-cleanup after some timeout.
Use Cases
- One-off code reviews: "review this diff" → done
- Batch processing: spawn 10 ephemeral sessions, each processes one file
- Fire-and-forget queries: "what's the status of X?" → answer in
out, session dies
Implementation
Flag in session creation: echo 'new claude /project ephemeral' | 9p write agent/ctl
Daemon marks session as ephemeral. When backend exits cleanly (not crash), transition to stopped or exited instead of auto-restart. Optionally auto-cleanup after N seconds.
Fan-Out Workflows
Send a prompt to multiple bots, collect responses, process them.
Pattern
- Sender writes to multiple
infiles (fan-out) - Receivers process, write to their
out, enterawait - Sender polls status files, waits for all to reach
await(oridle?) - Sender reads all
outfiles (fan-in) - Sender summarizes/processes responses, continues workflow
Example: Dev → {Security Auditor, Code Reviewer}
dev writes to security/in: "audit this change"
dev writes to reviewer/in: "review this change"
dev polls security/state and reviewer/state
when both == "await":
dev reads security/out
dev reads reviewer/out
dev processes feedback, applies changes
Coordination
- Manual: orchestrator script polls status, issues prompts
- Conductor bot: LLM-driven coordination (see Conductor Bot idea)
- Built-in: daemon could support fan-out primitives (write to multiple sessions atomically, block until all respond)
FIFO Semantics
out as FIFO: write buffers until read, next write clears previous. Orchestrator must read before next write or data is lost. That's a coordination bug, not a mechanism problem - orchestrator owns sequencing.
Status: await vs idle
await: bot has written toout, waiting for consumer to readidle: bot is ready for next prompt
If we use await, transition back to idle happens when orchestrator reads out (or after timeout?). If we use idle, orchestrator must track "has this bot written output since I last read?" separately.
await is cleaner - explicit signal that output is ready.
Chat Log in out
Bot writes final response or action summary to agent/{session-id}/out when done, just as it writes idle to state.
Benefits
- No need to attach to tmux to see what bot did
- Scriptable: read
outto get bot's answer - Enables pull-based workflows (see Pull Instead of Push idea)
- UI can display
outcontent directly
What to Write
- Final response: full text of bot's last message
- Action summary: "Applied changes to 3 files, ran tests, all passed"
- Both: summary + full response, delimited
Implementation
Backend writes to out before transitioning to idle. Daemon doesn't need to change - backends control what they write.
For Kiro/Claude, wrapper script or backend integration captures final output and writes to out fd.
Interaction with FIFO Semantics
If out is a FIFO that clears on next write, chat log is ephemeral - only the most recent response is available. That's fine for pull-based workflows. If we want persistent chat history, need separate log file or append-only out.
Context Utilization State
Daemon polls backend for context usage and exposes it via agent/{session}/usage.
Backend-Specific Queries
Kiro CLI: Send /context show as prompt, parse response for token usage (e.g., "17426/200000 (8.7%)")
Claude/Others: Write "N/A" or "-" (no context query support)
Polling Strategy
Daemon or monitor process:
- Every 1-2 minutes, check
agent/{session}/backend - If Kiro: send
/context show, parse output, write tousage - If unsupported: write "-" to
usage
Avoids interrupting user prompts. Cache last known value between polls.
9P Filesystem
Add to each session:
agent/<id>/usage # "17426/200000 (8.7%)" or "N/A"
UI Integration
- Assist: display usage in session list, color-code by threshold
- anvillm/anvilweb: add usage column
- Color scheme: green (<50%), yellow (50-80%), red (>80%)
Automatic Actions
When usage exceeds threshold:
- Warn user at 80%
- Suggest restart or conversation compaction at 90%
- Optionally auto-restart (preserves session ID, fresh context)
Conversation Compaction
High usage workflow:
- Summarize conversation history (via bot or separate summarizer)
- Restart session with summary as initial context
- Usage drops, session continues with same ID