symlock — symbol-level conflict prevention
symlock lets parallel coding agents reserve the symbol (function / class /
method) they're about to edit, so two agents never modify the same region and
collide at merge time. It is a single Rust binary that reads/writes a shared
.symlock/locks.json at the repo root and uses cross-process file locking, so
claims from agents in different worktrees are race-free.
Your job when this skill is active: claim before you edit, respect conflicts, release when done.
Prerequisites (check once)
- Binary on PATH: run
symlock --version. If missing, install it:cargo install --git https://github.com/echoVic/symlock(or grab a prebuilt binary from the repo's Releases page, or build from source withcargo build --releaseand usetarget/release/symlock). - Store exists:
symlock statusshould succeed. If it errors with "no .symlock directory", runsymlock initat the repo root once (coordinate — only one agent needs to init). - Identify yourself: set an agent id via
export SYMLOCK_AGENT=<your-id>(e.g. your worktree/branch name). Then you can omit--agenton every call. - Working in a git worktree? Worktrees are separate directory trees, so they
won't find the main checkout's
.symlock. Point every agent at one shared store:export SYMLOCK_DIR=/path/to/main/repo/.symlock. This is what makes claims visible across worktrees.
The workflow — follow this order every time you edit code
List symbols in the file you intend to change:
symlock symbols path/to/file.tsUse the exact symbol names it prints (methods are qualified, e.g.
TokenStore.issue).Claim each symbol you will edit, before writing any code:
symlock claim path/to/file.ts login- Exit 0 → the lock is yours. Proceed to edit.
- Exit 2 → CONFLICT. Another agent holds an overlapping region. Do not edit it. Read the reported holder, then either pick a different symbol, work on a different file, or tell the user these tasks overlap and should be serialized. Never wait-loop silently or force past a conflict.
Edit only the symbols you successfully claimed.
Release when the work on those symbols is done (e.g. before handing off or finishing the task):
symlock release --symbol login path/to/file.ts # one symbol symlock release # all of my claims
Rules
- Always claim before editing a function/class/method when this skill is active. An edit without a prior successful claim defeats the whole point.
- A conflict is a stop sign, not a retry prompt. On exit 2, change your plan — don't poll until it frees up unless the user asked you to.
- Claim at the tightest scope. Claim the specific method you'll touch, not the whole class, so other agents can work on sibling methods. (Claiming a class does lock all its methods — only do that if you're rewriting the class.)
- Release promptly so you don't block others longer than needed.
- Unsupported files (exit 1 "unsupported file type"): symlock only parses TS/JS, Python, Go, and Rust today. For other files, fall back to coordinating at file granularity with the user / orchestrator; do not assume it's safe.
Machine-readable mode
Add --json to any command for structured output (useful when driving symlock
from a script or orchestrator). A conflict prints a conflicts_with array
naming the holding agent and the overlapping line range.
symlock --json claim path/to/file.ts login
Reconciling after the fact (semantic merge)
If two branches/worktrees did edit the same file and you need to combine them,
use symlock merge instead of hand-resolving adjacent-line conflicts:
symlock merge --base base.ts --ours ours.ts --theirs theirs.ts -o merged.ts
- Exit 0 → merged cleanly (the two sides changed disjoint symbols). Use it.
- Exit 2 → it refused to guess (same symbol changed on both sides, or an import/structure change). The output has git-style conflict markers — resolve them yourself. Trust this refusal: it means the merge was not provably safe.
Quick reference
| command | purpose | exit codes |
|---|---|---|
symlock init |
create .symlock/ at repo root (once) |
0 / 1 |
symlock symbols <file> |
list lockable symbols | 0 / 1 |
symlock claim [--agent id] <file> <symbol> |
reserve a symbol | 0 ok · 2 conflict · 1 error |
symlock release [--agent id] [--file f] [--symbol s] |
drop claims | 0 / 1 |
symlock status |
show all active claims | 0 / 1 |
symlock merge --base b --ours o --theirs t [-o out] |
conservative semantic 3-way merge | 0 clean · 2 conflict · 1 error |