writ 发布 Rust 并行编码智能体协调层
rmems/writ
rmems/writ 是一个 Rust 编写的、与 provider 无关的并行编码智能体协调层,用同主机 SQLite 存储登记任务、智能体会话、仓库、检出、分支与 head,并通过租约跟踪归属。
A Rust-first, provider-neutral coordination layer for parallel coding agents.
Cursor, Claude Code, Codex, and similar harnesses already spawn workers and give each one an isolated checkout. What they don't share is coordination: who owns which task, branch, and worktree; what paths each worker intends to touch; who is blocked on whom; and how to hand work off without losing WIP. writ supplies that shared coordination layer so parallel workers can see each other, avoid duplicate work, negotiate overlapping edits early, and recover cleanly from pauses, crashes, and timeouts.
What writ does
- Identity and ownership. Registers tasks, agents/sessions, repositories, checkouts, branches, and heads in a shared SQLite store, and tracks which worker owns what via leases.
- Visibility. Declared paths and live status let workers see overlap before it becomes a merge conflict — overlap on separate branches is advisory and negotiated, not a fleet-wide freeze.
- Communication. Intent, dependency, blocker, help-request, and handoff messages so workers coordinate directly instead of through a human relay.
- Recovery. Pauses, timeouts, and stale leases are handoff events that preserve WIP and branch history — never permission to seize a live worktree or erase changes.
- Local integration.
git merge,rebase, andcherry-pickof peer work into an assigned feature branch are normal collaboration tools.
What writ does not do
- Does not own worker lifecycles. Harnesses (Cursor, Claude Code, Codex, plain
git worktree add) create and isolate checkouts.writ worktree registerrecords them; it never creates, moves, fetches, resets, or deletes a checkout. - Does not assign work. Task decomposition and scheduling belong to the manager agent and the tracker.
- Does not replace GitHub or Linear. Linear is the task tracker for teams that use it. GitHub is the source, PR, review, checks, and protected-branch merge authority.
- Does not silently allow duplicate ownership. Two live owners of the same task is a detected collision, not a race.
- Local only. SQLite coordination is same-host. Other transports are out of scope until implemented and tested.
How it fits with Cursor / Claude / Codex
Cursor / Claude Code / Codex / other harness
owns worker spawn + checkout/worktree lifecycle
│
│ register existing checkouts
▼
┌──────────────────────────────────────────┐
│ writ (Rust binary → writ-core) │
│ SQLite coordination store, same host │
│ │
│ tasks/agents · checkouts/branches/heads │
│ ownership leases · declared paths │
│ intent/dependency/blocker/help/handoff │
│ pause/timeout/recovery · status │
└──────────────────────────────────────────┘
│
▼
git / gh / OS primitives
│
▼
GitHub (source, PRs, reviews, checks)
Linear (task tracking)
Workers keep their harness-native isolation. writ is the shared memory and message bus between them — the part none of the harnesses provide.
One end-to-end example
-
The harness creates a checkout for a Linear task and registers it:
git -C /path/to/repo worktree add /path/to/job-42 -b task/RM-42-example origin/main writ --json worktree register /path/to/job-42 --job RM-42
Registration is coordination-state only — any path works, including a standalone clone; a branch ahead of its base registers as-is.
-
The worker declares its intended paths and picks up a lease. A second worker whose paths overlap sees the collision, identifies the owner, and negotiates a split or a handoff instead of silently diverging.
-
Work proceeds in the assigned checkout. Compatible peer work can be integrated locally (
git merge/cherry-pickinto the assigned branch). Commands routed through the safety boundary are checked first:writ git-safe --expected-branch task/RM-42-example --repo <worktree> status writ gh-safe -R acme/example-org pr view 1
-
On a pause, crash, or timeout, the lease plus declared state let another worker resume or take over without losing WIP.
-
The worker opens a PR; GitHub checks and reviews run; Linear tracks the task. Shared status stays truthful for managers and workers throughout.
Status
| Area | Today | Next |
|---|---|---|
writ CLI (git-safe, gh-safe, worktree, supervisor, status, attribution) |
Implemented | Envelope-compatible additions only |
Checkout registration (worktree register/unregister/inspect/list) |
Implemented | Managed lifecycle (create/remove/prune) is deprecated |
Claude Code hook dispatcher / writ install |
Implemented | Live burn-in outstanding (#124) |
| SQLite lease store, agent registry, crash consistency | Implemented | Additional lease policies remain additive |
| Same-host claims / messages | Implemented | Additional transports are out of scope |
| Declared paths / overlap visibility, handoff protocol | Implemented | Reuses the lease store — no second database |
| Path-scoped admission, lease budgets | Planned | #167 |
| Owner-allowlist enforcement | Enforced in writ-core |
#146 |
Canonical command names live in SKILL.md and writ --help. This README summarizes them.
Install
Install the binary for coordination and process helpers. The skill is optional portable agent guidance; ordinary contribution does not require installing either helper.
Prerequisites
- Git
- GitHub CLI authenticated (
gh auth status) - Stable Rust from rustup. The workspace MSRV is Rust 1.97.1 (pinned in
rust-toolchain.toml) - An agent host that loads skills from
~/.agents/skills(or another skill root — see Other agent hosts)
Binary
git clone https://github.com/rmems/writ.git "$HOME/src/writ" cd "$HOME/src/writ" cargo build --workspace cargo install --path crates/writ writ --help
Override the binary later with WRIT_BIN if it is not on PATH.
Skill (symlink, preferred)
Agents load SKILL.md from a skill root. Symlink the clone so edits stay in one tree:
mkdir -p "$HOME/.agents/skills" ln -s "$HOME/src/writ" "$HOME/.agents/skills/writ"
Copy is the fallback when a symlink is awkward (some Windows setups):
mkdir -p "$HOME/.agents/skills" cp -R "$HOME/src/writ" "$HOME/.agents/skills/writ"
A copied tree will drift from upstream; prefer the symlink when you can. Publishing via a skills CLI is not required for local use; packaging and hook-install narrative are #18.
Older docs used ~/.agents/skills/worktrees-hives. Use writ as the skill directory name now.
Verify
ls "$HOME/.agents/skills/writ/SKILL.md"
writ --helpThen confirm the agent host lists a writ skill (the exact UI depends on the host: skill list, /skills, or equivalent). If the file is present but the host does not show the skill, the host is not reading ~/.agents/skills.
Other agent hosts
Skill directories are not standardized. Common roots include ~/.agents/skills, ~/.claude/skills, and a project-local skills folder. Cursor, Cline, Codex, and Claude Code each resolve skills differently. This README documents the ~/.agents/skills/writ primary path only. Point a copy or symlink of this repository at whichever root your host actually scans. A full install matrix is out of scope here (#18).
Quick start
Use the host's authorized native tools, or opt into writ helpers for shared ownership visibility, local integrity checks and bounded execution. Local integration is routine. Remote PR readiness, updates and merges follow GitHub permissions and operator authorization (AGENTS.md).
-
Install the binary when using these coordination helpers; the skill is optional.
-
The harness creates the isolated checkout (native worktree support, or plain git):
git -C /path/to/repo fetch origin git -C /path/to/repo worktree add /path/to/job-42 -b hive/issue-42-example origin/main
Then register it for coordination — any path works, including a standalone clone:
writ --json worktree register /path/to/job-42 --job job-42
Registration is coordination-state only: no creation, relocation, rename, fetch, or reset. A branch ahead of its base registers as-is.
-
If using the optional command helpers:
writ git-safe --expected-branch hive/issue-42-example --repo <worktree> status writ gh-safe -R acme/example-org pr view 1
-
Inspect collaboration status from the shared lease store:
writ --json status
Do not expect a discover → add → check-all → list hive loop. Those were scaffold-era skill stubs and were removed with the Python orchestrator. Current operator flow is: the harness isolates a worktree, writ worktree register joins it to the coordination store, the worker integrates compatible peer work locally when needed, implements in that tree, opens a PR, optionally hands it to babysit-pr, and reports readiness. A remote merge requires operator authorization and GitHub permission.
Architecture
Two layers, one binary.
| Layer | Owns | Does not own |
|---|---|---|
| Local integrity helpers | Checkout/branch identity, WIP preservation, force-with-lease-only pushes, explicit repository targeting, process containment | Task assignment, harness checkout placement, remote merge permissions |
| Coordination state (same host) | Agents, leases, declared paths, overlaps, messages, and handoffs in one SQLite store | Cross-host transport, task decomposition, or scheduling |
git, gh, OS |
Version-control, GitHub, and process primitives, invoked through allowlists | Policy |
Leases are the join: coordination state that the enforcement layer checks at write time. Phase 1 ships SQLite (leases.db, WRIT_LEASE_PATH) with a prepare/inspect/reconcile protocol so a crash between checkout registration and the lease row can be classified without adopting unproven ownership. Budget columns are reserved; fix_cycles is the only accumulated counter and uses the same prepare/commit journal. Enforcement of budgets is #167.
┌─────────────────────────┐
│ Agent host │
│ loads SKILL.md │
└───────────┬─────────────┘
│
┌───────────▼─────────────┐
│ Orchestrator session │
│ (host agent + writ CLI) │
└───────────┬─────────────┘
┌────────────────┼────────────────┐
▼ ▼ ▼
Worktree A Worktree B Worktree C
Worker subagent Worker subagent Worker subagent
issue → PR babysit …
│ │ │
└────────────────┼────────────────┘
▼
Durable coordination state
SQLite leases.db (status/list/register)
Call-outs:
- Isolation: workers must not share a dirty worktree. One writable worker per assigned worktree and branch.
- Babysit: success means merge-ready (CI green, mergeable, reviews clean, threads resolved). Readiness does not itself authorize a merge.
- Stacks: handle from the bottom of the stack upward. Do not run parallel writers on one stack.
- State location: collaboration status reads the SQLite lease store (
WRIT_LEASE_PATH, else platform user-datawrit/leases.db).watched.json(WRIT_STATE_PATH) is a leftover reader only and is not the status authority.
Why hooks
Enforcement runs as Claude Code hooks: writ install writes the hook block into .claude/settings.json, and writ hook dispatches the JSON payloads. The checked-in .claude/settings.json has no startup hooks or tracker dependency. Writ hooks are opt-in; validate them in a throwaway repository before installing into shared settings.
PreToolUse— "Exit 2 means a blocking error… exit 2 blocks whether or not you print JSON: even a JSONpermissionDecisionofallowcan't override it."SubagentStart/SubagentStop— agent registry.
WorktreeCreate/WorktreeRemove are deliberately not installed: worktree lifecycle is harness-owned. If an older settings file still routes them to writ hook, the dispatcher treats them as coordination-only (register/release records, never filesystem mutation).
Register with writ install. Matcher scope starts at Bash(git *) and Bash(gh *) only.
Glossary
| Term | Meaning |
|---|---|
| Orchestrator | The host agent session that loads SKILL.md, calls writ, and spawns workers. It is not a writ subcommand. |
| Worker | A subagent bound to one assigned worktree and branch. Workers integrate peer work locally and perform remote actions only within operator authorization. |
| Worktree | An isolated git checkout created by the harness (or plain git worktree add), registered with writ worktree register. Any path; no writ-specific root required. |
| Watchlist / hive | Legacy name. Live visibility is writ status / writ worktree list over the SQLite lease store. |
| Merge-ready | CI green, conflict-free, required checks successful, review threads resolved. Report it; merge only when authorized. |
| Lease | SQLite coordination row for a registered checkout (WRITER_LOCKED / UNASSIGNED today). |
Commands
Implemented writ surface (writ --help is authoritative):
| Command | Status | Operator meaning |
|---|---|---|
writ status / writ jobs |
Implemented (read-only) | Collaboration snapshot from the SQLite lease store. See docs/status-schema.md. |
writ ci classify |
Implemented | Classify gh pr checks / statusCheckRollup JSON. Emits residual codes plus a compact collaboration view for status consumers. Does not write state. |
writ git-safe … |
Implemented | Run a git command after the allowlist and, for mutations, expected-branch checks. |
writ gh-safe … |
Implemented | Run a gh command with configured repository-target and local-checkout checks. GitHub decides remote permissions. |
writ supervisor run --timeout <secs> … |
Implemented | Spawn a child with wall-clock, idle, and grace recovery (--idle/--stall, --grace, --progress-secs, env WRIT_SUPERVISOR_*; see docs/timeout-policy.md). Timeout is a handoff residual: Unix SIGTERM-then-SIGKILL on the process group; Windows kills only the direct child (grandchildren may survive). Never deletes a harness checkout. |
writ watchlist list|check|check-all |
Implemented (view) | Collaboration status over leases.db plus optional live GitHub overlay. Does not persist a watchlist file. |
writ worktree register|unregister|inspect|list |
Implemented | Coordination records for harness-owned checkouts. Register/unregister never touch files or branches. |
writ lease inspect|reconcile |
Implemented | Read-only identity report and crash recovery for interrupted registration. Never deletes unproven checkouts. |
writ coord announce|show|list|inbox|send|ack|pause|handoff |
Implemented | Same-host claims/messages on leases.db. Path overlap is advisory; pause does not seize WIP. |
writ worktree create|remove|prune |
Deprecated | Managed lifecycle kept for caller compatibility during the transition. create requires --schema-version 2 and --start-point. |
writ attribution format |
Implemented | Render review and collaboration replies with real agent/task/branch/session identity. Include a SHA only when one exists; never invent one. |
writ --json |
Implemented | Envelope-producing commands emit versioned JSON on stdout (generic v1; status/jobs v2); diagnostics on stderr. Exceptions: hook --json has no envelope; git-safe/gh-safe validation failures and install failures emit stderr only. See the CLI contract and fixtures. |
writ install / hook dispatcher |
Implemented | Registers writ hook into .claude/settings.json; burn-in outstanding (#124). |
JSON envelopes look like:
{"ok":true,"schema_version":1,"command":"cli.bootstrap","data":{},"error":null}Policy rejections print to stderr and exit 2 (they do not wrap a JSON envelope):
$ writ --json git-safe push --force writ: policy violation [BARE_FORCE_PUSH]: bare --force/-f is not allowed; use --force-with-lease only
Coordination model
Identity
A lease row joins a task, an agent/session, and a checkout path — the "who owns what" record. writ worktree register creates it for an existing checkout; unregister releases it while keeping identity so a verified reclaim can prove ownership. TTL expiry marks a lease stale; it never erases the checkout's WIP.
Overlap and handoff
Declared-path claims provide early overlap visibility through the existing lease store. Overlap on separate branches is advisory: the colliding worker identifies the owner and negotiates a split, a sequence, or a handoff. Duplicate live ownership of one task is a detected collision, not silent divergence. A stale lease is a recovery/handoff event — not permission to kill a worker or discard its changes.
Communication
Same-host coordination records intent, dependency, blocker, overlap, help, handoff, and acknowledgement messages in the existing store, with owner generations to distinguish stale handoffs from live state. A manager can split scope or pick one integration owner without turning every message into a human approval gate. Cross-host transport remains out of scope.
Local integration
git merge, git rebase, and git cherry-pick of peer branches into the assigned branch are routine; conflict repair is expected. Resolve the contended files, validate the combined result, and publish the resulting head/dependencies for other workers. A merge that would overwrite uncommitted WIP must be refused first — commit, stash, or abort. GitHub controls remote protected-branch integration; a local branch name is not a separate permission gate.
GitHub vs Linear
| Tool | Role |
|---|---|
| Linear | Optional task tracker: task identity, status, planning, when the operator uses it. |
| GitHub | Source of truth for code, PRs, reviews, checks, and protected-branch merges. |
| writ | Same-host coordination state only. No task-tracker clone, no merge authority. |
Use the task context already supplied; no tracker account, issue twin or mirror is required.
Safety invariants
Rust helpers enforce their own local integrity checks:
- Force pushes may use only
--force-with-lease; bare--forceand-fare forbidden. - Each job edits only its assigned branch and isolated checkout; direct supervised git mutations verify the expected branch. The harness chooses checkout placement.
- One writable worker per assigned worktree and branch; stacked PRs are handled bottom-up.
- Unbounded fix loops are a named failure class (F2); lease budgets are planned in #167.
Helpers and hooks are opt-in. Supervision accepts host scripts and interpreters; it does not recursively inspect their subprocesses or opaque API payloads. Harness permissions and GitHub authorization remain authoritative. Deprecated managed-create commands retain their own path sandbox.
Owner allowlist
Writ's GitHub helpers check supported explicit target selectors against a configured owner allowlist. GitHub permissions authorize remote access.
- Set
WRIT_ALLOWED_OWNERS=acme,example-org(comma-separated), or pass--allowed-owners/ explicit owners at the API boundary. - An empty allowlist denies owner-taking operations (
writ worktree createandghcommands that select a repository via-R/--repoin any pflag spelling, or viaGH_REPO) rather than permitting them. gh apichecks literal owners inrepos/OWNER/REPO/...,orgs/OWNER/repos, andusers/OWNER/repos, includingapi.github.comURLs. Other API endpoint families and owners hidden in placeholder expansion, GraphQL node IDs or opaque request bodies are outside that inference; configured explicit repository selectors remain checked.- HTTP(S) URL scheme and DNS hostname matching are case-insensitive. Numeric ports, userinfo and a trailing DNS dot do not hide the target host. REST URL inference matches
api.github.comexactly; PR/issue URLs retain owner checks on valid public and enterprise DNS hosts. Extracting an owner does not authorize the host. Malformed HTTP authorities are rejected. - Comparison uses the same host/case normalization as
github_repo_slugs_match, soAcme/Repoandgithub.com/acme/repocannot diverge.
Examples use generic owners such as acme and example-org.
Related skills
| Skill | Role |
|---|---|
writ (SKILL.md) |
Fleet procedure: register checkouts, coordinate workers, local integration, issue → PR. |
babysit-pr (installed companion) |
Single-PR interactive monitoring in the current checkout: CI, reviews, threads, merge-ready report. |
Build and gates
Run focused checks while working. Use these native gates and appropriate independent review before claiming readiness; checkpoints report their narrower evidence. External findings need actionable evidence:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspaceInstall the agent skill
SKILL.md is the portable procedure (not a security boundary). Clone once, then symlink that directory into a skill root so <root>/writ/SKILL.md resolves. Shared hub:
git clone https://github.com/rmems/writ.git "$HOME/src/writ" "$HOME/src/writ/scripts/install-skill.sh" test -f "$HOME/.agents/skills/writ/SKILL.md" && echo OK
./scripts/install-skill.sh --root all also links Grok, Cline, Claude Code, and Cursor roots. The installer is idempotent, creates parent directories, and refuses to clobber a non-symlink without --force. Restart the agent (or start a new session) and list skills, or run /skills writ.
Full root table, uninstall, verification, and WSL notes: docs/install.md.
This is not writ install, which writes the writ hook block into .claude/settings.json (implemented; do not run it against a shared settings file until the #124 burn-in completes).
Runtime paths
| Purpose | Default | Override |
|---|---|---|
| Deprecated managed-worktree root (not a supervisor constraint) | platform user-data writ/worktrees |
WRIT_WORKTREE_BASE, else WH_WORKTREE_BASE |
| Job worktree | {worktree root}/{owner}/{repo}/{job_id} |
Deprecated managed lifecycle only; register accepts any path |
| Lease store | platform user-data writ/leases.db |
WRIT_LEASE_PATH |
| Watched state (legacy reader) | platform user-data writ/watched.json |
WRIT_STATE_PATH, else WH_STATE_PATH |
| Rust binary | writ on PATH |
WRIT_BIN |
If the new writ data root is absent and a pre-rename worktrees-hives root still exists, the path resolver keeps using the legacy root so an upgrade does not hide existing state. That is a read/fallback, not an automatic directory move.
Troubleshooting
| Symptom | What to check |
|---|---|
Agent does not see the writ skill |
ls "$HOME/.agents/skills/writ/SKILL.md"; confirm the host's actual skill root; recreate the symlink. |
writ: command not found |
cargo install --path crates/writ from the clone, or set WRIT_BIN. |
| writ status / writ jobs is empty | Expected until a checkout is registered. Register with writ worktree register. See docs/status-schema.md; for the live GitHub overlay use writ watchlist (docs/watchlist-schema.md). |
| policy violation [BARE_FORCE_PUSH] or [MERGE_BLOCKED] | Exit 2 is the safety boundary working. Use --force-with-lease only when allowed. MERGE_BLOCKED covers local checkout/WIP protections, not GitHub merge authorization. Preserve WIP before local integration. |
| Owner allowlist did not block another org | Confirm WRIT_ALLOWED_OWNERS / --allowed-owners is set. Empty lists deny. The gate covers worktree create and gh repo selectors, not host MCP calls. |
Issue labels and templates
Canonical product labels (use these; do not invent new names unless the epic
expands the taxonomy). Prefer docs over GitHub's default documentation.
Keep GitHub label descriptions identical to this table (commands in
CONTRIBUTING.md):
| Label | Description |
|---|---|
epic |
Multi-issue umbrella / milestone grouping |
core |
Coordination primitives (registration, leases, messages, handoff, status) |
orchestrator |
Multi-subagent scheduling, caps, join/report |
docs |
README, SKILL.md, templates, operator docs |
platform |
Install paths, multi-agent-host packaging, validation on second host |
safety |
Force-with-lease, fix caps, owner allowlist, WIP preservation |
Optional GitHub issue forms live in .github/ISSUE_TEMPLATE/:
Feature (new capability), Bug (unexpected failure), Chore (hygiene,
packaging, docs-only). Each asks for Summary, Problem / context, Acceptance
criteria checkboxes, and the Linear footer documented in
CONTRIBUTING.md.
Roadmap and tracking
- Product direction and task tracking for this repo: Linear (
writ/RMtickets). Using Linear is a convention of this repository's maintainers, not a product requirement. - GitHub issues mirror actionable work items; they are optional, not a required workflow.
- Open coordination work: crash-consistent lease/ownership records and the same-host claim/overlap/message/handoff layer build on the existing SQLite lease store.
- Lease budgets (fix-loop bound): #167
- Hook burn-in: #124 · Install narrative: #18 · Owner allowlist: #146
Project documentation
AGENTS.md— the authoritative contribution, autonomy, and safety contractCONTRIBUTING.md— issue templates and canonical labelsSKILL.md— portable agent procedure (guidance, not a security boundary)docs/install.md— clone + symlink skill install, cross-agent roots, uninstalldocs/cli-contract.md— command, JSON envelope, exit-code, persistence, and lifecycle compatibility matrixREVIEW.md— pull-request lifecycle and review checklistdocs/ci-taxonomy.md— Class A/B/C CI check policy for companion-skill monitoringdocs/adr/0001-rust-only-v1-runtime-and-babysit-pr-boundary.md— v1 Rust-only runtime and Codexbabysit-prboundarydocs/workflows/safe-issue-verified-commit.md— issue → verified pushdocs/workflows/safe-verified-commit-to-pr.md— verified push → PR handoffdocs/status-schema.md—status/jobsJSONdocs/timeout-policy.md— supervisor hang recovery (hard/idle/lost-child, grace kill, redispatch budget)docs/watchlist-schema.md—watchlistcollaboration viewdocs/examples/— captured response envelopesdocs/hook-boundary.md— hook-boundary contract tests and two-hook burn-in (#81)
License
Licensed under the Apache License 2.0.
来源:GitHub · github.com