Metadata-Version: 2.4
Name: laneguard
Version: 0.2.0
Summary: Coordinate parallel coding agents: disjoint backlogs and conflict prediction for in-flight branches.
License: MIT
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"

# laneguard

Keep parallel coding agents in their lanes.

- `laneguard plan TASKS_DIR` checks a backlog of task files for disjoint file ownership **before** work starts.
- `laneguard scan REPO` compares in-flight branches and predicts merge conflicts **before** integration.
- `laneguard mcp` exposes both to coding agents as MCP tools.

Standard library only. Git is used read-only. Output is deterministic and compact; `--json` is the contract.

**Status:** v0.2 includes the claims ledger, schema command, and packaging. See [docs/spec.md](docs/spec.md) for the specification and [AGENTS.md](AGENTS.md) if you are an agent working on this repository.

## Install

Install from GitHub now:

```bash
pip install "laneguard @ git+https://github.com/55kov/laneguard.git"
```

Once published to PyPI:

```bash
pip install laneguard
```

## Usage

Run these Bash examples from the repository root with Python 3.12+ available
as `python3` and Git installed. Setup creates only disposable files under `/tmp`;
`laneguard` itself uses Git read-only. Run the blocks in order. Each output block
contains actual stdout, including the exit status printed by `printf`.

### Plan: explicit task ownership

Create two disjoint tasks, including one file that does not exist yet:

```bash
example_dir=$(mktemp -d)
mkdir "$example_dir/tasks"
printf 'Files: README.md\n' > "$example_dir/tasks/docs.md"
printf 'Files: future.py\n' > "$example_dir/tasks/feature.md"
python3 -m laneguard plan "$example_dir/tasks"
printf 'exit: %s\n' "$?"
```

```text
plan: disjoint
exit: 0
```

The default text is concise. Use `--repo .` to classify claims against Git's
tracked files, and `--json` for the structured `exists`/`new` arrays (`--repo`
also prints classifications in text mode). New files are allowed:

```bash
python3 -m laneguard plan "$example_dir/tasks" --repo . --json
printf 'exit: %s\n' "$?"
```

```json
{"kind":"plan","version":1,"tasks":[{"id":"docs","files":["README.md"],"exists":["README.md"],"new":[]},{"id":"feature","files":["future.py"],"exists":[],"new":["future.py"]}],"overlaps":[],"warnings":[]}
```

```text
exit: 0
```

Add a competing claim to see an overlap:

```bash
printf 'Files: README.md\n' > "$example_dir/tasks/other.md"
python3 -m laneguard plan "$example_dir/tasks"
printf 'exit: %s\n' "$?"
```

```text
overlap docs other: README.md
exit: 2
```

### Scan: a fully specified clean fixture

This creates a temporary bare repository with an empty base commit and two local
task branches pointing to it. Neither branch has changes. The fixture setup
writes only to the temporary repository, never the project repository:

```bash
git init --bare --quiet "$example_dir/repo.git"
git -C "$example_dir/repo.git" fast-import --quiet <<'GIT'
commit refs/heads/main
mark :1
committer Example <example@example.com> 0 +0000
data 7
fixture

reset refs/heads/task/one
from :1

reset refs/heads/task/two
from :1

GIT
python3 -m laneguard scan "$example_dir/repo.git" --base main --branches 'task/*' --detail
printf 'exit: %s\n' "$?"
```

```text
scan: no likely conflicts
task/one: (no changes)
task/two: (no changes)
exit: 0
```

`--detail` lists each selected branch's changed files; omit it for compact
findings only. Use `--json` for structured hunk ranges and merge-base hashes.
Branches editing the same file in separate regions receive an informational
`shared-file` verdict, which does not cause exit 2. Overlapping ranges or ranges
within `--near` lines (default 3), and delete/rename versus modify cases, can
produce `likely-conflict` and exit 2.

For both `plan` and `scan`, exit **0** means no overlaps or likely conflicts,
**2** means overlaps or likely conflicts were found, and **1** means a usage,
input, or Git error. Exit 2 is a finding, not a command failure.

### MCP: a finite stdio round trip

MCP uses newline-delimited JSON-RPC 2.0. Pipe a concrete `ping` request; closing
stdin terminates the server at EOF:

```bash
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"ping"}' | python3 -m laneguard mcp
printf 'exit: %s\n' "$?"
```

```text
{"jsonrpc":"2.0","id":1,"result":{}}
exit: 0
```

The server also supports `initialize`, `tools/list`, and `tools/call` for
`laneguard_plan` and `laneguard_scan`. Tool arguments mirror the CLI inputs (for example,
`tasks_dir` for plan and `repo`, `base`, `branches`, `near`, `detail` for scan).
Tool results already include JSON, so no `json` argument is needed. CLI finding
exit codes apply to CLI invocations; MCP returns findings and tool errors in
JSON-RPC responses.

### Claims

Claim your task's files in a shared ledger before starting work.
If another owner holds an overlapping path, wait for their release before claiming
it and making changes.
Concurrent writers are serialized by an exclusive `FILE.lock` (retrying for up to
10 seconds, reclaiming locks older than 60 seconds), and writes atomically replace
the ledger using a temporary file in the same directory.

```bash
python3 -m laneguard claim --ledger "$example_dir/claims.json" --owner docs README.md
printf 'exit: %s\n' "$?"
python3 -m laneguard claim --ledger "$example_dir/claims.json" --owner feature README.md
printf 'exit: %s\n' "$?"
python3 -m laneguard release --ledger "$example_dir/claims.json" --owner docs README.md
printf 'exit: %s\n' "$?"
```

```text
claimed docs: README.md
exit: 0
refused feature README.md: held by docs (README.md)
exit: 2
released docs: README.md
exit: 0
```

List the live claims as JSON, trimming the temporary ledger path from the output:

```bash
python3 -m laneguard claims --ledger "$example_dir/claims.json" --json | python3 -c 'import json, sys; d = json.load(sys.stdin); del d["ledger"]; print(json.dumps(d, separators=(",", ":")))'
printf 'exit: %s\n' "$?"
```

```json
{"kind":"claims","version":1,"claims":[],"refused":[],"released":[],"warnings":[]}
```

```text
exit: 0
```

### Schemas

Print the packaged JSON Schema for `plan`, `scan`, or `claims`; here are the first
ten lines for `plan`:

```bash
python3 -m laneguard schema plan | head -n 10
printf 'exit: %s\n' "$?"
```

```text
{
  "$id": "https://laneguard.dev/schemas/plan.schema.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "additionalProperties": false,
  "properties": {
    "kind": {
      "const": "plan",
      "type": "string"
    },
    "overlaps": {
exit: 0
```

In CI, save the full `laneguard schema <kind>` output and validate the corresponding
`--json` output with a JSON Schema draft 2020-12 validator.

Remove the disposable fixtures when finished:

```bash
rm -rf "$example_dir"
```

## Development

```bash
python -m venv .venv && .venv/bin/pip install -e '.[dev]'
PY=.venv/bin/python sh check.sh
```
