ERGORS provides git-based workspace management for coordinating project files across a distributed node network. This enables:
- Fractal Task Isolation: Each task gets its own git worktree, enabling parallel development
- Cryptographic Identity: Node ED25519 keys convert to SSH format for git authentication
- P2P Coordination: Workspace sync messages coordinate file transfers over SSH git
- Cnidarium Integration: Workspace and task metadata persists in the node's state store
| Layer | Transport | Data |
|---|---|---|
| Git | SSH | Project files agents work on |
| Cnidarium | Local | Task assignments, node registry, session logs |
| P2P Channels | Commonware | Coordination messages (sync triggers, status) |
~/.ergors/
├── data/cnidarium/ # Internal state (task metadata, node identities)
├── workspaces/ # Git-managed project workspaces
│ ├── {project-a}/ # Project repository
│ │ ├── .git/
│ │ ├── main/ # Primary worktree (default branch)
│ │ └── tasks/ # Task-specific worktrees
│ │ ├── task-{uuid}/ # Isolated task branch
│ │ └── ...
│ └── {project-b}/
│ └── ...
└── ssh/ # Node SSH keys (derived from ED25519 identity)
├── id_ed25519 # Private key for git auth
└── id_ed25519.pub # Public key
# Register a new workspace (clone from remote)
ergors workspace add my-project --remote git@github.com:org/repo.git
# Register a local workspace (no remote)
ergors workspace add local-project
# List all registered workspaces
ergors workspace list
ergors workspace list --limit 20
# Show workspace details
ergors workspace show <workspace_id>
# Remove a workspace
ergors workspace remove <workspace_id>
ergors workspace remove <workspace_id> --force # Remove even with active tasks
# Sync with remote
ergors workspace sync <workspace_id>
ergors workspace sync <workspace_id> --push # Push local changes
ergors workspace sync <workspace_id> --remote upstream # Specify remote# Create a task worktree (isolated branch)
ergors workspace task create <workspace_id>
ergors workspace task create <workspace_id> --task-id my-task-123
ergors workspace task create <workspace_id> --assign-to <node_id>
# List active task worktrees
ergors workspace task list
ergors workspace task list --workspace <workspace_id>
ergors workspace task list --node <node_id>
# Complete a task (commit and optionally merge)
ergors workspace task complete <task_id> --message "Implement feature X"
ergors workspace task complete <task_id> --message "Fix bug Y" --merge
# Fail/abandon a task
ergors workspace task fail <task_id> --reason "Requirements changed"
ergors workspace task fail <task_id> --reason "Blocked" --cleanupAll commands support --json for scripting:
ergors workspace list --json | jq '.workspaces[].name'
ergors workspace task list --json | jq '.worktrees[] | select(.status == "active")'┌─────────────────┐
│ Workspace Added │ Clone/init project repository
└────────┬────────┘
│
▼
┌─────────────────┐
│ Task Created │ Create worktree on branch task/{task_id}
└────────┬────────┘ Cnidarium: store TaskWorktree record
│
▼
┌─────────────────┐
│ Task Active │ Agent works on files in isolated worktree
└────────┬────────┘ Changes stay local to the worktree
│
┌────┴────┐
▼ ▼
┌───────┐ ┌───────┐
│Complete│ │ Fail │
└───┬───┘ └───┬───┘
│ │
▼ ▼
┌───────────────────┐ ┌──────────────────┐
│ Commit + Merge │ │ Discard worktree │
│ Push to remote │ │ Update status │
└───────────────────┘ └──────────────────┘
- Coordinator registers workspace and assigns tasks to executors
- Executor receives task, creates worktree, performs work
- Executor completes task, commits, sends
PUSH_NOTIFYover P2P - Coordinator receives notify, fetches changes via SSH git
- Coordinator merges to main, sends
MERGE_COMPLETEto network
Node ED25519 keys are exported to SSH format for git authentication:
NodePrivKey (ED25519) ──► ~/.ergors/ssh/id_ed25519 (OpenSSH format)
Git commits are signed with the node key, providing cryptographic chain of custody.
| Field | Value |
|---|---|
| Name | Node type (e.g., "coordinator", "executor") |
{node_id_short}@ergors.local |
Example commit:
Author: executor <a1b2c3d4@ergors.local>
Signed-by: ED25519:a1b2c3d4e5f6...
| Action | Direction | Purpose |
|---|---|---|
FETCH_REQUEST |
Executor → Coordinator | Request latest from remote |
PUSH_NOTIFY |
Executor → Network | Notify peers of new commits |
MERGE_COMPLETE |
Coordinator → Network | Task branch merged to main |
CONFLICT |
Any → Coordinator | Merge conflict needs resolution |
Workspace sync messages flow over Channel 2 in the commonware network layer.
message WorkspaceMetadata {
string workspace_id = 1;
string name = 2;
string remote_url = 3; // Origin remote (empty if local-only)
string local_path = 4; // Path under ~/.ergors/workspaces/
bytes head_commit = 5; // Current HEAD SHA
string default_branch = 6; // e.g., "main"
google.protobuf.Timestamp created_at = 7;
google.protobuf.Timestamp last_synced = 8;
}message TaskWorktree {
string task_id = 1;
string workspace_id = 2;
string branch = 3; // task/{task_id}
string worktree_path = 4;
bytes base_commit = 5;
TaskWorktreeStatus status = 6;
string assigned_node_id = 7;
google.protobuf.Timestamp created_at = 8;
}
enum TaskWorktreeStatus {
UNSPECIFIED = 0;
CREATING = 1;
ACTIVE = 2;
COMMITTING = 3;
MERGING = 4;
COMPLETED = 5;
FAILED = 6;
}message WorkspaceSync {
string workspace_id = 1;
SyncAction action = 2;
string branch = 3;
bytes commit_hash = 4;
string sender_node_id = 5;
google.protobuf.Timestamp timestamp = 6;
}
enum SyncAction {
UNSPECIFIED = 0;
FETCH_REQUEST = 1;
PUSH_NOTIFY = 2;
MERGE_COMPLETE = 3;
CONFLICT = 4;
}| Prefix | Value Type | Description |
|---|---|---|
workspaces/{id} |
WorkspaceMetadata | Workspace registry |
task_worktrees/{task_id} |
TaskWorktree | Task worktree records |
// Workspace operations
storage.put_workspace(&metadata).await?;
storage.get_workspace("workspace_id").await?;
storage.list_workspaces(limit).await?;
storage.delete_workspace("workspace_id").await?;
// Task worktree operations
storage.put_task_worktree(&worktree).await?;
storage.get_task_worktree("task_id").await?;
storage.list_task_worktrees(workspace_filter, node_filter).await?;
storage.delete_task_worktree("task_id").await?;service ManagementService {
// Workspace management
rpc AddWorkspace(AddWorkspaceRequest) returns (AddWorkspaceResponse);
rpc ListWorkspaces(ListWorkspacesRequest) returns (ListWorkspacesResponse);
rpc GetWorkspace(GetWorkspaceRequest) returns (GetWorkspaceResponse);
rpc RemoveWorkspace(RemoveWorkspaceRequest) returns (RemoveWorkspaceResponse);
rpc SyncWorkspace(SyncWorkspaceRequest) returns (SyncWorkspaceResponse);
// Task worktree management
rpc CreateTaskWorktree(CreateTaskWorktreeRequest) returns (CreateTaskWorktreeResponse);
rpc ListTaskWorktrees(ListTaskWorktreesRequest) returns (ListTaskWorktreesResponse);
rpc CompleteTaskWorktree(CompleteTaskWorktreeRequest) returns (CompleteTaskWorktreeResponse);
rpc FailTaskWorktree(FailTaskWorktreeRequest) returns (FailTaskWorktreeResponse);
}packages/
├── ho-std/src/git/
│ ├── mod.rs # Module exports
│ ├── identity.rs # ED25519 → SSH conversion
│ ├── repository.rs # Git repo operations
│ ├── workspace.rs # WorkspaceManager (worktree lifecycle)
│ └── registry.rs # WorkspaceRegistry
│
├── ergors/src/
│ ├── git/
│ │ ├── mod.rs # Engine-side git module
│ │ └── coordinator.rs # P2P sync coordination
│ ├── grpc/
│ │ └── management.rs # Workspace gRPC handlers
│ ├── storage.rs # Cnidarium workspace methods
│ └── network/
│ └── manager.rs # WorkspaceSync message handling
│
└── ergors/src/
├── commands/
│ └── workspace.rs # CLI workspace commands
└── client/
└── mod.rs # gRPC client methods
┌─────────────────────────────────────────────────────────────┐
│ ergors │
│ workspace add/list/sync/task create/complete/fail │
└─────────────────────────┬───────────────────────────────────┘
│ gRPC
▼
┌─────────────────────────────────────────────────────────────┐
│ ManagementService │
│ AddWorkspace, CreateTaskWorktree, SyncWorkspace, etc. │
└─────────────────────────┬───────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌───────────┐ ┌─────────────────┐
│ ErgorsStorage │ │ Git Ops │ │ SyncCoordinator │
│ (Cnidarium) │ │ (libgit2) │ │ (P2P messages) │
└─────────────────┘ └───────────┘ └─────────────────┘
# 1. Register the workspace
ergors workspace add my-project --remote git@github.com:myorg/myproject.git
# 2. Create a task worktree
ergors workspace task create ws_abc123 --task-id feature-auth
# 3. Work on files in the worktree
cd ~/.ergors/workspaces/my-project/tasks/task-feature-auth/
# ... make changes ...
# 4. Complete the task
ergors workspace task complete feature-auth --message "Add authentication" --merge# On coordinator node:
ergors workspace add shared-repo --remote git@internal:org/shared.git
ergors workspace task create ws_shared --task-id implement-api --assign-to node_executor_1
# On executor node (receives task via P2P):
# Automatically creates worktree at ~/.ergors/workspaces/shared-repo/tasks/task-implement-api/
# ... executor agent works on files ...
# Executor completes:
ergors workspace task complete implement-api --message "Implement REST API"
# Coordinator receives PUSH_NOTIFY, fetches, and merges#!/bin/bash
# Check for stuck tasks older than 1 hour
stuck=$(ergors workspace task list --json | jq -r '
.worktrees[]
| select(.status == "active")
| select((.created_at | fromdateiso8601) < (now - 3600))
| .task_id
')
for task in $stuck; do
echo "Failing stuck task: $task"
ergors workspace task fail "$task" --reason "Timeout" --cleanup
done# ho-std/Cargo.toml
git2 = "0.19" # libgit2 bindings
ssh-key = "0.6" # ED25519 → SSH format
# ergors/Cargo.toml
uuid = { version = "1.0", features = ["v4"] }
hex = "0.4"# Verify SSH key was generated
cat ~/.ergors/ssh/id_ed25519.pub
# Test SSH connection to remote
GIT_SSH_COMMAND="ssh -i ~/.ergors/ssh/id_ed25519" git ls-remote git@github.com:org/repo.git- Check workspace exists:
ergors workspace show <id> - Verify git repo is valid:
git -C ~/.ergors/workspaces/<name>/main status - Check for existing task:
ergors workspace task list --workspace <id>
- Check remote connectivity
- Verify SSH key has push access
- Check for conflicts:
git -C <worktree_path> status - Review engine logs:
tail -f ~/.ergors/logs/engine.log | grep -i workspace