Bundle
@ericjiang0423/dsh-orchestrator
Local-first issue board for DeepSeek Harness: board data, model-facing tools, an agent planning loop, and a human approval queue for agent-proposed issues.
- Source
- EricJiang0423
- stars
- 1 stars
- License
- Apache-2.0
- Updated
- Updated 14 days ago
Readme
<div align="right">
English · [中文](README-zh.md)
</div>
<!-- AUTO-GENERATED -->
<h1 align="center">dsh-orchestrator</h1>
<p align="center">
<strong>Local-first issue orchestrator for DeepSeek Harness: one board per repository, a fresh session per issue, and a scheduler that works the queue itself</strong>
<br />
<em>Cordis Plugin · Workspace-Scoped Board · Per-Issue Sessions · Auto-Pull Scheduler · Two Human Approval Gates</em>
</p>
<p align="center">
<a href="#quick-start"><img src="https://img.shields.io/badge/Quick_Start-4CAF50?style=for-the-badge" alt="Quick Start" /></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache--2.0-D97706?style=for-the-badge" alt="License" /></a>
</p>
<p align="center">
<img src="https://img.shields.io/badge/TypeScript-3178C6?style=flat&logo=typescript&logoColor=white" alt="TypeScript" />
<img src="https://img.shields.io/badge/Node.js-339933?style=flat&logo=node.js&logoColor=white" alt="Node.js" />
<img src="https://img.shields.io/badge/React-20232A?style=flat&logo=react&logoColor=61DAFB" alt="React" />
<img src="https://img.shields.io/badge/Zod-3068B7?style=flat&logo=zod&logoColor=white" alt="Zod" />
<img src="https://img.shields.io/badge/esbuild-FFCF00?style=flat&logo=esbuild&logoColor=black" alt="esbuild" />
<img src="https://img.shields.io/badge/Cordis-2D2D2D?style=flat" alt="Cordis" />
</p>
---
## Features
| Feature | Description |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| One Board Per Repository | The board is bound to the session's working directory, not to the conversation: two sessions in the same repo share a board, and a session in another repo sees its own — the host resolves it, so the browser can never mix projects |
| Fresh Session Per Issue | "Work on this" opens a brand-new session with the issue's brief and binds the issue to it, so every issue's transcript and cost are its own — and several can run at once |
| Auto-Pull Scheduler | A scheduler keeps up to N issues in flight and refills from `todo` by itself — highest priority first — with live controls for concurrency and the auto-pull toggle in the board header |
| Three Human Gates | An agent can never move an issue out of `proposed`, can never mark one `done`, and can never shelve one as `archieved`: proposals need your approval, finished work needs your acceptance, and archiving accepted work is yours too — all enforced in the service layer, not the UI |
| Durable Approval Queue | Agent-proposed issues land in a `proposed` column and stay there until a human approves or rejects them — durable across restarts, unlike a one-shot approval prompt |
| Board as a Chat Peer | The board registers into the conversation view ring, so it appears as a tab beside Chat and Trajectory instead of a separate page |
---
## Screenshots
The board rendered with sample data — no project details from any real deployment.
**Board view**: workspace-scoped board with the approval queue, the scheduler strip (auto-pull toggle, parallelism, live running/waiting counts), and the session chip on the in-flight issue:

**Issue detail**, expanded from a card, showing the unified acceptance controls — Accept, or Send back with a reason that lands as a comment:

---
## Quick Start
### Prerequisites
- Node.js 22.5+
- A running [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) profile
### Install from source (recommended)
Clone, build, and link the checkout into the profile. `dsh plugin add .` from inside the checkout registers the local build, so later `npm run build` runs are picked up without reinstalling:
```bash
git clone https://github.com/EricJiang0423/dsh-orchestrator.git
cd dsh-orchestrator
npm install && npm run build
dsh plugin --profile web add .
```
`lib/` is gitignored build output. `npm test` builds it automatically when it is missing (a `pretest` hook runs `npm run build` first), so on a fresh clone you can run tests right after `npm install` without building by hand. Once `lib/` exists, `npm test` skips the rebuild to stay fast — run `npm run build` explicitly to test your latest source changes.
### Install from npm (registry)
> ⚠️ The unscoped `dsh-orchestrator` on the registry belongs to an unrelated project (zibo/dsh-agent-mesh). This project is published under a scope:
```bash
dsh plugin --profile web add @ericjiang0423/dsh-orchestrator
```
### Run
```bash
dsh --profile web
```
---
## Usage
### Capture an issue without leaving the chat
```
/task Fix the flaky checkout test
```
### Call the board from another plugin
```ts
import type {} from '@ericjiang0423/dsh-orchestrator'
export const inject = ['taskboard']
export function apply(ctx: Context) {
const open = ctx.taskboard.listTasks({ status: 'todo' })
}
```
### Call the RPC endpoint
```ts
const res = await fetch('/_dsh/taskboard/rpc', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
method: 'task.update',
params: { id, patch: { status: 'in_review' }, expectedVersion: 3 },
}),
})
```
### Configure the scheduler and the planning loop
```yaml
- id: taskboard
config:
scheduler:
concurrency: 2
autoPull: true
plan:
maxRounds: 16
maxHandoffChars: 8192
```
---
## Architecture
```mermaid
%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '14px'}}}%%
graph LR
UI[Board View<br/>React] -->|RPC + SSE| SVC[Taskboard Service<br/>Cordis Plugin]
TOOLS[taskboard_* Tools<br/>Model-facing] --> SVC
PLAN[taskboard_plan<br/>Workflow Engine] -->|fresh subagents| TOOLS
SVC --> DB[(Storage Domain)]
SVC --> WS[Workspace Registry<br/>cwd → workspace]
SCHED[Scheduler<br/>session-link] -->|agents.create| ISS[Per-Issue Sessions<br/>ctx.agents]
SVC --> SCHED
ISS --> SVC
classDef client fill:#3B82F6,stroke:#2563EB,color:#fff,stroke-width:2px
classDef service fill:#10B981,stroke:#059669,color:#fff,stroke-width:2px
classDef data fill:#8B5CF6,stroke:#7C3AED,color:#fff,stroke-width:2px
class UI client
class SVC,TOOLS,PLAN,SCHED,ISS service
class DB,WS data
```
The browser half never talks to storage directly. Every read and write goes through `ctx.taskboard`, whether the caller is the board's own RPC route, a model-facing tool, the planning loop, or the scheduler — so the two human gates (no self-approval, no self-acceptance) live in one place and apply to every caller. The scheduler is the only thing that starts work on its own, and it draws exclusively from `todo`, which only a human can put an issue into.
---
## Configuration
| Key | Default | Description |
| --------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `scheduler.concurrency` | `1` | How many issues may run at once; changeable live from the board header |
| `scheduler.autoPull` | `true` | Whether the board pulls from `todo` on its own; changeable live from the board header |
| `scheduler.sweepIntervalMs` | `30000` | Safety-net sweep that frees slots occupied by vanished sessions |
| `plan.subagentProvider` | `spawn` | Fresh structured-output subagent provider used for every planning round |
| `plan.maxRounds` | `32` | Default AND ceiling for one `taskboard_plan` run; a call may lower it, never raise it |
| `plan.maxHandoffChars` | `16384` | Maximum serialized characters in one round's structured report; an oversized report fails the run instead of being truncated |
| `plan.maxIssues` | `16` | Maximum issues admitted into one planning run |
---
## API
The browser half talks to the host half over one endpoint, `POST /_dsh/taskboard/rpc`, with `{ method, params }` in the body, rather than one REST path per resource. DeepSeek Harness's typed RPC layer requires build-time code generation this plugin's build does not run, so the route is deliberately explicit — see [docs/spike-findings.md](docs/spike-findings.md) for why.
| Method | Description |
| --------------------- | ---------------------------------------------------------------------------------------------- |
| `board.view` | The board this session belongs to (resolved from its workspace), with live scheduler state |
| `project.list` | List every project |
| `project.create` | Create a project |
| `task.list` | List issues, optionally filtered by project, status, or session |
| `task.get` | Read one issue with its comments and activity trail |
| `task.create` | Create an issue |
| `task.update` | Change an issue; refuses a stale `expectedVersion` |
| `comment.create` | Add a comment to an issue |
| `task.start` | Open a FRESH session for one issue and hand it the work |
| `task.startNext` | Start the next `todo` issue — highest priority first — without naming one |
| `task.accept` | Accept finished work (`in_review` → `done`) — the human gate no agent can pass |
| `task.sendBack` | Send finished work back to `todo` with a reason (recorded as a comment), unbinding its session |
| `scheduler.configure` | Change concurrency or the auto-pull toggle; returns the resulting state |
Change notifications stream over `GET /_dsh/taskboard/events` as Server-Sent Events.
---
## Directory Structure
```
src/
├── client/ # Browser half
│ ├── board.tsx # BoardView: columns, cards, scheduler strip, approval + acceptance controls
│ ├── index.tsx # Client plugin entry, slot registration
│ ├── rpc.ts # fetch()-based RPC client + SSE subscription
│ └── styles.ts # Layout-only CSS; every color is a theme token
├── domain.ts # Zod schemas and the status machine
├── service.ts # ctx.taskboard: reads, writes, version CAS
├── rpc.ts # Host RPC route + SSE change stream
├── tools.ts # Model-facing taskboard_* tools
├── command.ts # /task human command
├── plan-loop.ts # taskboard_plan: the fixed planning loop
├── session-link.ts # Workspace resolution, per-issue sessions, the scheduler
├── skill.ts # Registers the manage-taskboard skill
├── actors.ts # Actor identity
├── wire.ts # Shared browser <-> host RPC types
└── index.ts # Plugin entry: mounts every face
test/ # node:test suites
skills/manage-taskboard/ # Bundled working-agreement skill
docs/ # Extension-point research notes
```
---
## Tech Stack
### Runtime
| Technology | Purpose |
| ----------- | ----------------------------------------------------------------------- |
| TypeScript | Source language for both plugin halves |
| Cordis | Host plugin framework: services, effects, dependency injection |
| Zod | Schema validation for the four storage-domain tables |
| Schemastery | Plugin `Config` validation |
| React | Board view rendering (peer dependency, supplied by the host at runtime) |
### Build & Test
| Technology | Purpose |
| ------------------- | ------------------------------------------------------------------------ |
| esbuild | Bundles the browser half into the client-module envelope the host serves |
| Node.js test runner | `node --test`, no test framework dependency |
---
## Contributing
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing`)
3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing`)
5. Open a Pull Request
---
## License
[Apache-2.0](LICENSE). The domain model and issue-flow rules derive from [dashi-taskboard](https://github.com/chuspeeism/dashi-taskboard) — see [NOTICE](NOTICE).
Install
dsh plugin --profile web add github:EricJiang0423/dsh-orchestrator
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install ericjiang0423-dsh-orchestrator from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.