JoyZoning is a human operator cockpit for multi-agent software workflows on your machine. It is not an IDE, not a chatbot wrapper, and not a VS Code clone.
Hermes is optional. Work can run as a managed lease (diet-hermes) or as external-agent JSDP (Cursor, Claude Code, manual) with the same verify + human merge gates. See execution-paths.md.
Product concepts (read first): concepts.md — operator cockpit, execution leases or external tasks, human merge.
Operational modes (do not collapse metaphors): operational-modes.md — Planning (kanban), Execution (managed + external), Review (merge queue), Habitat (ambient watch).
Terminal strategy: hermes-aligned-terminal-strategy.md — cognition vs authority: Hermes (optional) owns agent chat; JoyZoning owns runtime governance (jz). Chat does not hold final authority on merge or Complete.
Stack: .NET 8, Avalonia 12 desktop, ASP.NET Core control plane, SQLite + EF Core, SignalR, optional jz CLI.
flowchart TB
subgraph desktop [Desktop_UI]
ManagerChat[ManagerChat]
Kanban[Kanban]
ExecVP[ExecutionViewport]
Workspace[Workspace]
Approvals[Approvals]
Timeline[Timeline]
end
subgraph cp [ControlPlane_9470]
API[REST_API]
Hub[SignalR]
Orch[OrchestrationService]
DB[(SQLite)]
end
subgraph agents [Agent_Adapters]
HermesA[HermesAdapter]
DietA[DietCodeAdapter]
end
subgraph hermes [diet_hermes_8642]
Runs["/v1/runs + SSE"]
ApprovalsAPI["/v1/runs/id/approval"]
end
desktop --> API
desktop --> Hub
API --> Orch
Hub --> Orch
Orch --> DB
Orch --> HermesA
Orch --> DietA
HermesA --> Runs
DietA --> Runs
High-volume timeline mirroring uses a second SQLite file (broccoliq.db) via a localhost Node worker (:9471). See broccoliq.md. EF Core joyzoning.db stays authoritative; the hive is audit/index only.
JoyZoning uses one diet-hermes checkout (Hermes:InstallRoot in config). There is no second Hermes install for “master” vs “slave” — that split is session-based and kanban-synced:
| Role | UI surface | Backend (MVP) |
|---|---|---|
| Manager (lead) | Manager Chat, kanban routing | POST /v1/runs with manager toolsets on the shared gateway |
| Executor (worker) | Execution viewport, DietCode task runs | Same gateway and install; bounded executor toolsets per run |
The control plane and kanban board coordinate work between manager and executor sessions on the same :8642 API. DietCode does not use Firestore operator_api.py in MVP.
Each WorkTask runs in one of two modes (TaskExecutionMode):
| Mode | Who edits files | State carrier | Convergence |
|---|---|---|---|
| ManagedAgent | Hermes / DietCode via lease | ExecutionLease + HandoffPacket |
Lease → ReadyForReview → human merge |
| ExternalAgent | Cursor, Claude Code, manual | WorkTask external fields + workspace scan |
mark-ready → verify → external/complete |
Managed path — one active ExecutionLease per card:
Card → ExecutionLease (leased) → HandoffPacket → DietCode run (running)
→ verification → ready_for_review → human merge/revoke
External path — no lease row:
Card → external/start → branch joyzoning/card-<id> + stored prompt
→ IDE edits → external/ready-for-review → verification → external/complete
| Concept | Role |
|---|---|
ExecutionLease |
Managed only: bounded authority, branch, risk, status |
ExternalTaskExecutionService |
External only: branch, prompt, scan, gates |
HandoffPacket / external prompt |
Objective, JSDP sections, verification hints |
VerificationReport |
Evidence on lease (managed) or task (external) |
Rules (enforced in orchestrators + JsdpMergeGate):
- One active lease per card when managed; critical cards need
humanApprovedCriticalon dispatch. - External: branch match + diff gates on
ready-for-review; no API shortcut toComplete. - Agents cannot set task
Completeon bounded/external work — operatorjz task complete --yesonly. - JSDP chains: next role blocked until prior role is
Completeafter merge.
API (control plane): execution-orchestration-api.md (leases) · control-plane-api.md (external + delivery chains) · external-agent-jsdp.md.
Runtime enforcement: LeaseRuntimeService applies caps (MaxGlobalActiveLeases, MaxCriticalLeases), heartbeat staleness (LeaseStaleOptions), and absolute expiration before orchestrator mutations. Evidence is append-only JSON on the lease row (EvidenceLogJson).
Workspace execution (JSDP): WorktreePlanner plans the canonical session root with branch joyzoning/card-<task-id>. JsdpWorkspaceExecution enforces canonical checkout; legacy .joyzoning/worktrees/ and .joyzoning/live/ are pruned on merge/revoke. See philosophy.md.
- Renders six product surfaces
- Calls control plane REST on
127.0.0.1:9470 - Subscribes to SignalR
/hubs/operatorfor live events (Phase 1+)
- Owns operator state in SQLite (
~/Library/Application Support/JoyZoning/joyzoning.dbon macOS) - Normalizes Hermes SSE into
joy_eventsviaHermesRunEventConsumer - Correlates approvals to tasks and runs (
ApprovalService+approval_grants) - Spawns Hermes gateway/dashboard via
HermesConnectivityService/HermesDashboardConnectivityService - Hosted services:
KanbanAutoSyncHostedService— periodic Hermes kanban import when enabled in configLeaseReconciliationHostedService— stale leases, orphan runs, merged tasks with active leases
- On startup: marks interrupted executions; bootstraps runtime config from SQLite
Append-only joy_events + relational tasks, sessions, executions, approvals.
IAgentAdapter implemented by HermesAdapter and DietCodeAdapter (shared HermesHttpClient).
JoyZoning UI statuses map to Hermes kanban when sync is enabled:
| JoyZoning | Hermes kanban |
|---|---|
| Backlog | todo / triage |
| Planned | ready / scheduled |
| In Progress | running |
| Needs Approval | review + pending approval |
| Verifying | review |
| Blocked | blocked |
| Complete | done |
- Explicit orchestration — state machines, not hidden agent magic
- Human visibility — every risky action surfaces in Approvals
- Recoverability — SQLite survives restarts; interrupted runs marked
Interrupted - Local-first — loopback only; no cloud dependency for core operation
- Complement IDEs — workspace viewer + external editor, not replacement
Two surfaces share the same HTTP API. See hermes-aligned-terminal-strategy.md for the full cognition/authority split.
| Surface | Role |
|---|---|
jz / jz tui |
Operator shell — slash commands, live SignalR events, lease workflows; launches hermes --tui via jz hermes tui (does not reimplement agent chat) |
jz agent |
Constrained worker — heartbeat, verify, blocked, done → ready_for_review only |
LeaseContextResolver infers task/session from cwd (worktree), env vars, or single active lease. AgentGuard blocks forbidden raw paths and CLI subcommands in agent mode. JOYZONING_NO_TUI=1 forces JSON automation mode (no interactive REPL).
| Table | Purpose |
|---|---|
operator_sessions |
Workspace + Hermes profile scope |
work_tasks |
Kanban cards, risk, Hermes kanban link id |
execution_sessions / execution_steps |
DietCode run tracking |
execution_leases |
Lease lifecycle, handoff JSON, verification, evidence |
approval_requests / approval_grants |
Human approval inbox + scoped grants |
joy_events |
Append-only audit + replay cursor |
app_config |
Serialized UI/settings blob |
Migrations: JoyZoning.Persistence/Migrations/.
See configuration.md and src/JoyZoning.ControlPlane/appsettings.json:
Hermes:InstallRoot— path to diet-hermes checkoutHermes:ApiBaseUrl— defaulthttp://127.0.0.1:8642ControlPlane:ListenUrl— defaulthttp://127.0.0.1:9470LeaseRuntime:*— scheduler caps, durations, stale thresholds
- hermes-aligned-terminal-strategy.md — Hermes vs JoyZoning terminal roles
- concepts.md — why JoyZoning is structured this way
- use-cases.md — scenarios
- getting-started.md — first run
- hermes-integration.md — diet-hermes wiring
- lease-lifecycle.md — lease state machine
- desktop-ui.md — Avalonia surfaces
- development.md — build and test
- troubleshooting.md — common failures
- README.md — documentation index