{
  "markdown": "# rebar\n\n[![PyPI version](https://img.shields.io/pypi/v/nava-rebar)](https://pypi.org/project/nava-rebar/)\n[![Python versions](https://img.shields.io/pypi/pyversions/nava-rebar)](https://pypi.org/project/nava-rebar/)\n[![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)\n[![CI](https://github.com/navapbc/rebar/actions/workflows/test.yml/badge.svg)](https://github.com/navapbc/rebar/actions/workflows/test.yml)\n\n**An event-sourced, git-backed ticket store + Jira reconciler built for agent\nswarms — one store, exposed as a Python library, a **CLI**, and an **MCP** server.**\n\n![rebar's core loop: `rebar ready` → `rebar claim` → `rebar transition … closed`](docs/assets/rebar-demo.svg)\n\n- **Three surfaces, one store** — drive rebar as a **CLI** (`rebar`), a Python\n  library (`import rebar`), or an **MCP** server (`rebar-mcp`).\n- **The tracker lives in the repo** — tickets are an append-only event log on a\n  `tickets` git branch; no database, no daemon, and it travels with every clone.\n- **Built for parallel agents** — atomic claims, convergent merges, and provenance\n  links let many agents and sessions write at once without lost work.\n- **Optional LLM gates** — review a ticket's *plan* before work, its *completion*\n  before close, and its *code* before it merges.\n- **Bidirectional Jira sync** — a level-triggered reconciler keeps tickets and Jira\n  in step, so teammates stay in the loop.\n- **Dogfooded through two independent gates** — every change to rebar's own `main`\n  must pass an LLM code review **and** CI, on Gerrit, before it lands.\n\n## Install\n\n```bash\npipx install nava-rebar          # the `rebar` CLI (add [mcp] / [agents] for those extras)\nbrew install navapbc/rebar/rebar # or via Homebrew\n```\n\n## Quickstart\n\nRun one ticket end-to-end with the CLI or the Python library; the JSON block is the\nMCP server config so agents can drive the same loop over MCP. `rebar --help` (and\n`rebar <command> --help`) is the authoritative command reference.\n\n```bash\n# CLI: one ticket through init -> create -> ready -> claim -> close\nrebar init\ntid=$(rebar create task \"Add a login page\" -o json | python3 -c 'import json,sys; print(json.load(sys.stdin)[\"id\"])')\nrebar ready                                              # lists it as ready to work\nrebar claim \"$tid\"                                       # open -> in_progress\nrebar transition \"$tid\" in_progress closed              # in_progress -> closed\n```\n```python\nimport rebar                                            # the same loop via the Python library\ntid = rebar.create_ticket(\"task\", \"Add a login page\")\nrebar.claim(tid)\nrebar.transition(tid, \"in_progress\", \"closed\")\n```\n```json\n{ \"mcpServers\": { \"rebar\": { \"command\": \"uvx\", \"args\": [\"--from\", \"nava-rebar[mcp]\", \"rebar-mcp\"] } } }\n```\n\nThat's the whole loop — **init → create → ready → claim → close**. The CLI and Python\nblocks each drive **one** ticket end-to-end (the same id threaded through every step, no\nhard-coded id); the JSON is the MCP server config — add it to your client so an agent can\nrun the same loop via the MCP tools. State is shared through the repo so many agents (and\nteammates via Jira) coordinate without stepping on each other.\n\n## How it works\n\nrebar stores tickets as an **append-only event log** on a dedicated `tickets` git\norphan branch (worktree at `.tickets-tracker/`); ticket state is computed by replaying\nevents, and every write auto-commits and pushes so the store is shared immediately. A\n**level-triggered reconciler** bidirectionally syncs tickets with Jira. The branch name\nand worktree dir are configurable (`tracker.branch` / `tracker.dir` — see\n[Configuration](#configuration)). Reads stay sub-second into the thousands of tickets;\nfor measured numbers and git-growth expectations see\n[`docs/scale-envelope.md`](docs/scale-envelope.md).\n\n**Documentation** lives under [`docs/`](docs/README.md) — start with the\n[docs index](docs/README.md) (grouped by audience: user / operator / contributor /\nagent) or the day-to-day [user guide](docs/user-guide.md).\n\n## Why rebar\n\nIf you run coding agents against a repo, you eventually want to run *several* at\nonce — and the moment you do, they need a shared place to coordinate. Most\ntrackers weren't built for that:\n\n- **They're heavy.** A daemon to babysit or a local database to keep running,\n  with dependencies thick enough that a routine upgrade can break your work\n  tracking across machines.\n- **They don't travel with the code.** State lives outside the repo, so a fresh\n  clone doesn't come with its tickets.\n- **They fight your git history.** A tracker that writes to your working branch\n  tangles ticket churn into your source-code commits.\n- **They have no concurrency story.** Nothing stops two agents from claiming the\n  same work or clobbering each other's state, and concurrent edits produce merge\n  conflicts you resolve by hand — or lose.\n- **They buckle at scale.** Speed and usability fall off past a few hundred\n  tickets.\n\n**rebar's answer is to make the tracker part of the repo.** Tickets are an\nappend-only event log on a dedicated `tickets` orphan branch (linked in through a\ngitignored worktree); current state is a fast, deterministic replay of that log.\nThat single decision pays off across the board:\n\n- **Zero infrastructure, fully portable.** No database, no daemon — just git and a\n  lightweight Python install. Clone the repo and the tracker comes with it.\n- **No commit interference.** Ticket events live on their own branch and never\n  touch your source history. Every write auto-commits and auto-pushes, so activity\n  is shared in real time.\n- **Concurrency by design.** Each event gets a globally-unique filename, so\n  parallel writes merge as a clean union, and the rare conflicting fork resolves\n  deterministically — every clone converges with no lost data. `claim` is an\n  atomic, optimistic-concurrency primitive: agents grab work without stepping on\n  each other.\n- **Built to scale.** The event log plus cached replay stays fast as tickets grow.\n\nOn top of that foundation, rebar adds what parallel agent work actually needs:\n\n- **Bidirectional Jira sync** — agents work in rebar, teammates work in Jira, and\n  a level-triggered reconciler keeps the two in step. To run it **automatically** in\n  CI, see [docs/jira-sync-setup.md](docs/jira-sync-setup.md) (the GitHub Actions\n  reconcile-bridge + heartbeat-canary setup).\n- **Conflict-aware scheduling** — tickets record their file impact, so\n  `next-batch` hands parallel agents work that won't collide on the same files.\n- **Scratch space** — an invisible per-ticket channel for subagents to pass notes\n  to one another.\n- **Structural quality gates** — clarity, acceptance-criteria, dispatch-readiness,\n  and repo-wide health checks keep work dispatch-ready.\n- **LLM review gates** *(optional).* Review an agent's plan before work starts, its completion before the ticket closes, and its code before it merges. Plan-review and code-review share one four-pass kernel. A finder cites evidence, a separate verifier tests each claim with atomic yes or no questions, and deterministic policy decides what blocks. A passing plan or completion review records an operation certificate as a DSSE envelope. The envelope carries an SSHSIG signature over its PAE bytes, produced with the signing environment's Ed25519 key. The certificate names that environment as its principal. The result is a machine-checkable process record for the reviewed work.\n- **Provenance links** — `discovered_from` ties emergent work back to the ticket\n  that surfaced it.\n- **One store, three interfaces** — drive it from the CLI, a Python library, or\n  the MCP server.\n\n## Requirements\n\n**System prerequisites:**\n- [Python](https://www.python.org) ≥ 3.11\n- [`git`](https://git-scm.com) is a runtime prerequisite because the store uses a Git orphan branch and worktree. The runtime engine uses in-process Python and does not require `bash` or `jq`.\n- Git 2.38 or newer is required for development, tests, and CI. The floor is declared in `.github/git-version-floor.txt`. The test suite and CI fail when Git is below that floor.\n- Deployments that use the optional S3 repair path require Git 2.38 or newer because that path invokes `git merge-tree --write-tree`.\n- **No external lock binary is required.** Write serialization uses a two-window\n  lock built entirely from the Python standard library — a `fcntl.flock(LOCK_EX)`\n  advisory lock plus an atomic `mkdir` lock (`src/rebar/_store/lock.py`) — so there\n  is **no dependency on util-linux's `flock` binary** (or any other external tool).\n  The `mkdir` window keeps mutual exclusion holding even where `fcntl.flock` is\n  unreliable (e.g. some network filesystems).\n- [`acli`](https://developer.atlassian.com/cloud/acli/) (Atlassian CLI) — a\n  **required external binary for the Jira Cloud reconciliation/bridge path**: every\n  `bridge`/`reconcile` Cloud mutation shells out to it, so it must be installed on\n  `PATH` (install pointer: [docs/jira-sync-setup.md](docs/jira-sync-setup.md)). The\n  `[jira-datacenter]` Data Center path does **not** need it — it uses the `jira`\n  Python library.\n\n**Python dependencies.** A base install (`pip install nava-rebar`) provides the `rebar` CLI, the `import rebar` library, and the lean workflow engine. It installs [`pyyaml>=6`](https://pyyaml.org) for the workflow DSL loader, [`jsonschema>=4.18`](https://python-jsonschema.readthedocs.io) for schema registry and workflow input and output validation, and [`referencing>=0.30`](https://referencing.readthedocs.io) for JSON Schema `$ref` resolution. The engine core and reconciler otherwise use the Python standard library. All other dependencies are optional extras that are imported lazily to keep the base installation light. CI verifies this boundary.\n\n- **Optional runtime capabilities** — install what you serve:\n  - **`[mcp]`** installs the [`rebar-mcp` server](https://modelcontextprotocol.io) with `mcp>=1.28.1,<2` and `pyjwt[crypto]>=2.10,<3`.\n  - **`[agents]`** — the LLM agent-operations framework + agentic workflow steps\n    (`rebar review-plan`, the `code_review` workflow): the provider-agnostic\n    [pydantic-ai](https://ai.pydantic.dev) runtime (`pydantic-ai-slim[anthropic]`)\n    plus [`json-repair`](https://github.com/mangiucugna/json_repair).\n- **Development & authoring extras** — not needed to run or serve rebar:\n  - **`[tracing]`** — an [OpenTelemetry](https://opentelemetry.io) OTLP trace sink\n    (write-only; never read back into a rebar decision), for diagnostics.\n  - **`[dev]`** — the test/lint/type tooling ([pytest](https://docs.pytest.org),\n    [ruff](https://docs.astral.sh/ruff/), [mypy](https://mypy-lang.org),\n    [hatchling](https://hatch.pypa.io)). `pip install -e '.[dev]'` also\n    self-references `[agents]` so the validation tests **run** rather than skip, and\n    is **required to run the full test suite** (the interface-parity tests import the\n    MCP server, so they error — not skip — without `mcp`).\n  - **[Node/npm](https://nodejs.org)** — needed **only** for the workflow visual\n    editor's front-end: *rebuilding* its vendored bundle\n    (`src/rebar/llm/workflow/editor_assets/`, the bpmn-js editor) and running the\n    faithful editor **E2E tier** (`tests/e2e/`, which drives the real bpmn-io\n    libraries). Both are developer-only — the built bundle is committed/shipped and\n    the E2E tier self-skips when Node is absent — so neither the base install nor the\n    default test suite needs Node. See [docs/workflow-editor.md](docs/workflow-editor.md).\n\nSee [Install](#install) and [Tests](#tests).\n\n## Install\n\nrebar ships from one Python package — PyPI distribution **`nava-rebar`** (the\nimport package and commands stay `rebar` / `rebar-mcp`). Pick the channel that\nfits. (System prerequisites in all cases: `git` (≥ 2.38 to develop/test rebar; see\n[Requirements](#requirements)) and `python3` (≥ 3.11); write\nserialization uses a built-in `fcntl.flock` + `mkdir` lock with no external\nbinary; `acli` is required on `PATH` for the Jira Cloud reconciliation/bridge path\n(see [docs/jira-sync-setup.md](docs/jira-sync-setup.md)), while the\n`[jira-datacenter]` Data Center path does not need it.)\n\n### Homebrew (CLI)\n\n```bash\nbrew install navapbc/rebar/rebar\n# or: brew tap navapbc/rebar && brew install rebar\n```\n\nInstalls the `rebar` CLI (and the `rebar` library inside the formula's venv). For\nthe MCP server via Homebrew users, install the `[mcp]` extra with pipx/uvx below.\n\n### PyPI — pipx / pip\n\n**Runtime (prod) — install what you'll run:**\n\n```bash\npipx install nava-rebar              # isolated CLI on PATH: rebar (+ lean workflow engine)\npip  install nava-rebar              # library: import rebar  (runtime deps: pyyaml, jsonschema, referencing)\npip  install 'nava-rebar[mcp]'       # + MCP server: rebar-mcp\npip  install 'nava-rebar[agents]'    # + LLM agent ops + agentic workflow steps (rebar.llm)\npip  install 'nava-rebar[tracing]'   # + OTLP trace sink (write-only)\npip  install 'nava-rebar[agents,tracing]'        # the union, if you want it all\n```\n\nThe base install runs **scripted** workflows (`rebar workflow new/validate/show/run`)\nwith no extra; **agentic** workflow steps and `rebar review-plan` need `[agents]`. Authoring\na workflow **visually** — `rebar workflow edit <file>`, a local bpmn-js editor that\nround-trips the diagram back to the IR — also needs no extra and no Node/npm: the editor\nfront-end ships pre-built in the wheel and is served locally (no CDN). For what the\nengine is *for* — when to author a workflow vs a bespoke op, the YAML DSL, the\nthree-pass review pattern, and the prompt-library + eval seam — see\n[docs/workflow-engine.md](docs/workflow-engine.md); for visual editing specifically see\n[docs/workflow-editor.md](docs/workflow-editor.md).\n\nThe `[agents]` extra adds the optional **LLM agent operations framework** (`rebar.llm`). It provides tool-using agents that review tickets and code through the library, CLI, and MCP interfaces. Model classes are configured in `[tool.rebar.llm.model_classes]` or with `REBAR_LLM_<CLASS>_MODEL`, `REBAR_LLM_MODEL_PROVIDER`, and `REBAR_LLM_BASE_URL`. The single `REBAR_LLM_MODEL` variable is deprecated. See [docs/llm-framework.md](docs/llm-framework.md).\n\nThe `[agents]` extra installs the Anthropic provider used for Claude. Bedrock requires `nava-rebar[agents,bedrock]`. ChatGPT and OpenAI-compatible endpoints require `[agents]` plus `pydantic-ai-slim[openai]`. Gemini requires `[agents]` plus `pydantic-ai-slim[google]`. Core rebar never requires or imports the LLM stack unless an agent operation is used.\n\n### MCP server — from the MCP Registry\n\nListed in the [MCP Registry](https://registry.modelcontextprotocol.io) as\n**`io.github.navapbc/rebar`**. Registry-aware MCP clients can add it by that\nname; or register it directly in your client config (zero pre-install via\n`uvx`):\n\n```json\n{\n  \"mcpServers\": {\n    \"rebar\": {\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"nava-rebar[mcp]\", \"rebar-mcp\"],\n      \"env\": { \"REBAR_ROOT\": \"/path/to/your/repo\" }\n    }\n  }\n}\n```\n\n(Already pip/pipx-installed `nava-rebar[mcp]`? Use `\"command\": \"rebar-mcp\"`\ninstead.) Server flags: `REBAR_MCP_READONLY=1` exposes only read tools;\nmutating bridge tools (`bridge_run`, `bridge_sync`, `bridge_pause`, and\n`bridge_resume`) require `REBAR_MCP_ALLOW_JIRA_SYNC=1`. Both flags accept any\ncase-insensitive truthy value — `1`, `true`, or `yes` (surrounding whitespace\ntolerated); anything else (incl. unset) is off.\n\n#### Private-repo fetch credentials (code-reading gates)\n\nThe LLM code-reading gates `review_plan`, `verify_completion`, `review_code`, and `scan_spec` default to attested mode. They fetch the selected ref from `origin` and read an immutable snapshot at the pinned SHA. A server whose `REBAR_ROOT` points to a private repository therefore needs read credentials through a Git credential helper, deploy key, or token in the server clone. Without credentials, attested mode fails closed with a remediation message and disables terminal prompts through `GIT_TERMINAL_PROMPT=0`. `source=local` reads the in-place checkout without fetching and never signs the result. [The snapshot guide](docs/repo-snapshot-gates.md) documents these semantics, the operation-certificate trust model for DSSE envelopes that carry SSHSIG signatures over their PAE bytes, each produced with an environment's Ed25519 key and attributed to that environment, and the settings for temporary storage, disk-space thresholds, and EFS or NFS locking.\n\n### From source\n\n```bash\ngit clone https://github.com/navapbc/rebar && cd rebar\npip install .              # library + CLI (runtime deps: pyyaml, jsonschema, referencing)\npip install '.[mcp]'      # + MCP server (FastMCP)\n# Developing rebar itself — the full dev environment (test/lint/type tooling +\n# the agents stack so the LLM validation tests RUN, not skip), installed through\n# the committed uv.lock (or `make install`, which adds the pre-commit gate):\nuv sync --extra dev\n```\n\n> **pipx source installs and older uv.** If `pipx install \"<path>[agents]\"` stops\n> before installing rebar with `pipx needs uv>=0.9.17`, it selected an older host uv.\n> Retry the same source install with pipx's pip backend:\n>\n> ```bash\n> pipx install --backend pip \"<path>[agents]\"\n> ```\n>\n> This is a host pipx/uv toolchain mismatch, not a rebar packaging defect.\n\n> **Contributing changes?** GitHub is a **read-only mirror** — `main` only advances via\n> Gerrit's two-vote gate (`LLM-Review` + `Verified`/CI). New contributors: start with the\n> friendly walkthrough [docs/your-first-change.md](docs/your-first-change.md); the full\n> reference is [CONTRIBUTING.md](CONTRIBUTING.md) (clone from Gerrit, push to\n> `refs/for/main`, then land it with a plain Gerrit **Submit** once both votes pass — `main`\n> is Rebase-If-Necessary, so Gerrit rebases onto the tip and submits server-side).\n\n> **Packaging note — why rebar installs *unpacked* to disk.** The library, CLI,\n> MCP server, and the whole read/write core run **in-process** in Python. The one\n> component that runs as a subprocess is the Jira **reconciler**, which ships under\n> `src/rebar/_engine/` as package **data** (`python -m rebar_reconciler`, plus the\n> `jira-capability-probe.py` script and the alias wordlist): it is launched and\n> read from the filesystem as real on-disk files, so the package must be installed\n> unpacked to a real directory and **zipimport / zip-safe bundles (zipapp, shiv,\n> PEX, Lambda zips) are unsupported**. Every standard install satisfies this:\n> pip/pipx wheels (hatchling builds unpacked), editable installs, and Homebrew all\n> land real files. `engine_dir()` asserts the engine dir is present on disk at the\n> first reconciler call and fails loudly otherwise.\n\n> **Advanced (optional) — gate commits with self-hosted code review.** Not needed\n> for standard rebar use. If you want *every* commit to `main` automatically\n> LLM-reviewed before it can land, you can self-host Gerrit + the rebar review-bot on\n> AWS (the bot imports the same `rebar.llm` review kernel the MCP server exposes) and\n> demote GitHub to a read-only mirror that only advances via Gerrit after the\n> `LLM-Review` vote passes. See [docs/gerrit-aws-setup.md](docs/gerrit-aws-setup.md) for\n> the server setup. *(This repo runs exactly that setup — see the contributor note above\n> and [CONTRIBUTING.md](CONTRIBUTING.md).)*\n\n## CLI\n\nThe **complete, always-current command reference** for every subcommand is [docs/cli-reference.md](docs/cli-reference.md). It is generated from the CLI's own help data. The essentials are `rebar init` → `rebar create <type> \"<title>\"` → `rebar ready` → `rebar claim <id>` → `rebar transition <id> <current> <target>`.\n\nRun `rebar help`, `rebar --help`, or `rebar -h` for the subcommand overview. Run `rebar <subcommand> --help` or `rebar help <subcommand>` for a specific subcommand. For leaf commands, an exact `--help` or `-h` token in any position before `--` prints usage without executing the command. Nested command families retain child routing. A leading help flag prints family help, while forms such as `rebar bridge preview --help` and `rebar audit show --help` print child help.\n\nRepo root is resolved from `REBAR_ROOT`, falling back to the git toplevel of the\nworking directory.\n\n**Structured output.** Every data-returning command emits machine-readable JSON\nvia the canonical `--output json` flag (short `-o json`; `--output llm` gives a\ntoken-minified shape for `show`/`list`/`ready`). Each distinct JSON shape is\ndocumented by a JSON Schema and validated across the CLI, library, and MCP in CI.\nSee [docs/output-schemas.md](docs/output-schemas.md) for the per-command contract\nand the schema source-of-truth.\n\n**Claiming work.** `rebar claim <id>` atomically moves an open ticket to `in_progress`. When `--assignee` is omitted, `claim` uses `ticket.default_assignee`. An explicit `--assignee` overrides that setting and must be a Jira-resolvable email or accountId.\n\n**Repo-wide health with `validate`.** `rebar validate` takes **no ticket id** — it\nscans the whole store and prints an overall tracker-health score (1-5, exit 0-4)\nbucketed into critical / major / minor / warning findings (`--output json`,\n`--terse`, `--verbose`). Passing it a ticket id errors. (rebar also has\n*per-ticket* structural gates that each take an `<id>` and verify a ticket is\n*shaped* like dispatchable work — every type needs an `## Acceptance Criteria`\nchecklist. See the ticket template and gate reference in\n[docs/plan-review-criteria-guide.md](docs/plan-review-criteria-guide.md).)\n\n**Closing work.** `rebar transition <id> in_progress closed` closes a task, bug, story, or epic. A bug close also requires `--class`. Completion verification is optional and disabled by default. Set `verify.require_completion_verification_for_close = true` to run it as part of each ordinary work-ticket close before the status change is recorded.\n\n**Links.** `rebar link <id1> <id2> <relation>` requires one of seven relations. They are `blocks`, `depends_on`, `relates_to`, `duplicates`, `supersedes`, `discovered_from`, and `caused_by`. `rebar unlink <source> <target> [relation]` accepts an optional relation. Without a relation, it removes the most recently created active link for that ordered pair. With a relation, it removes exactly that relation and preserves other relations between the pair. Blocking links may be promoted up the parent hierarchy when created, so `unlink` must target the promoted ancestor endpoint.\n\nA passing plan-review or completion-verifier records an **operation certificate**. The certificate is a DSSE envelope that carries an SSHSIG signature over its PAE bytes, produced with the signing environment's Ed25519 key. Its principal identifies that environment. The code-review gate reports its verdict through the review system and does not create this ticket certificate. The `rebar sign` command and library surface can also attach a manifest certificate outside those two gates. See [docs/manifest-signing.md](docs/manifest-signing.md).\n\n### Hierarchy promotion of blocking links\n\nFor **blocking** dependencies only (`blocks`, `depends_on`), rebar promotes the link endpoints up the parent hierarchy so the dependency sits between tickets at a comparable level (epic↔epic, story↔story, task/bug↔task/bug). When it does so, it emits a `REDIRECT: A→B promoted to …` note. Non-blocking relations (`relates_to`, `duplicates`, `supersedes`, `discovered_from`, `caused_by`) are linked exactly as given, with no promotion.\n\n### The store auto-commits and auto-pushes every write\n\nEvery rebar **write** (`create`, `edit`, `transition`, `claim`, `link`, …)\nauto-commits its event to the `tickets` branch **and** auto-pushes that branch to\n`origin/tickets` whenever an `origin` remote exists. **Local ticket activity is\ntherefore shared with the remote immediately** — including test/scratch tickets,\nso be deliberate when working against a repo with a shared `tickets` remote. The\npush is **best-effort**: with no `origin` remote nothing is pushed, and a push\nfailure (e.g. non-fast-forward it cannot auto-merge, or no network) never fails\nthe write — it leaves the local commit intact and the branch diverged.\n`rebar fsck` reports `PUSH_PENDING` when the local `tickets` branch is ahead of\n`origin/tickets`, so unpushed activity is observable. See\n[`docs/concurrency.md`](docs/concurrency.md) for the push/merge-retry algorithm.\n\n**Running locally, offline, or read-only.** This auto-sync is configurable when you\ndon't want the store talking to a remote (the full key set and env names are in\n[`docs/config.md`](docs/config.md)):\n\n- **`sync.push`** (env `REBAR_SYNC_PUSH`) — `always` (default) pushes each write\n  synchronously; `async` pushes in the background so per-write network latency\n  doesn't serialize a batch; `off` keeps commits **local** and never pushes (`fsck`\n  still surfaces `PUSH_PENDING`).\n- **`sync.pull`** (env `REBAR_SYNC_PULL`) — `on` (default) lets reads fetch from the\n  remote (the [freshness policy](#reads-share-one-freshness-policy-across-cli-library-and-mcp)\n  below); `off` gives a pure-local replay (offline work, tight loops, or right after\n  a write that already synced). Pass `--no-pull` to a single read subcommand for the\n  same effect (e.g. `rebar list --no-pull`).\n- **`mcp.readonly`** (env `REBAR_MCP_READONLY=1`) — serves only read tools over MCP,\n  so no writes — and therefore no commits or pushes — happen at all.\n\nHow big can it get? Reads stay sub-second into the thousands of tickets; writes\nare bounded by the per-event git commit (~25–30/s). See\n[`docs/scale-envelope.md`](docs/scale-envelope.md) for representative measured\nnumbers, git-growth expectations, and the compaction/maintenance commands, and\n[`docs/import-export.md`](docs/import-export.md) for bulk NDJSON export/import.\n\n### Reads share one freshness policy across CLI, library, and MCP\n\nEvery **read** — `show`, `list`, `ready`, `search`, `deps` — first runs a\nthrottled (**≤1/min**), best-effort `git fetch` + reconverge of the local\n`tickets` branch with `origin/tickets`, so a read reflects collaborators' pushes\nwithin at most a minute. This is **one contract shared by all three interfaces**:\nCLI, library (`rebar.list_tickets()`, …), and the MCP read tools all resolve\nthrough a single read implementation. (Previously only CLI reads synced, leaving\nMCP — the primary agent surface — with the *stalest* reads; that divergence is\ngone.) To skip this fetch for a pure-local replay, set `sync.pull=off` or pass\n`--no-pull` — see [Running locally, offline, or read-only](#the-store-auto-commits-and-auto-pushes-every-write)\nabove. Only the network fetch/merge is affected; the local reduce/cache path is\nunchanged. See\n[`docs/concurrency.md`](docs/concurrency.md#read-freshness-policy-uniform-across-cli-library-and-mcp).\n\n### The on-disk store is not human-readable — read it with `rebar`\n\nThe `tickets` branch is rebar's **internal storage format, not a document for\npeople to read.** Each ticket is a directory of append-only JSON **event** files\n(`${hlc}-${uuid}-${TYPE}.json`); the current state of a ticket is what you get by\n**replaying** those events through the reducer. Two consequences follow:\n\n- **It isn't laid out in order.** Event files are named by a Hybrid Logical Clock\n  + UUID and merge across clones as a union, so the files for one ticket are not a\n  top-to-bottom narrative — they are an unordered set that only becomes meaningful\n  after the reducer sorts and folds them. A single `EDIT`/`STATUS`/`TAG_DELTA`\n  file in isolation tells you a delta, not the ticket.\n- **The current state is computed, never stored.** Nothing on the branch holds the\n  compiled \"current\" ticket except a local, rebuildable `.cache.json` (gitignored).\n  Reading the raw files by hand will mislead you — a later event may supersede an\n  earlier one, a `SNAPSHOT` may fold many away, and concurrent forks resolve by a\n  deterministic rule you'd have to apply yourself.\n\nSo **don't `cat` the `.tickets-tracker/` worktree to find out where a ticket\nstands** — use the read commands, which run the reducer for you: `rebar show\n<id>`, `rebar list`, `rebar deps <id>`, `rebar search <query>` (CLI), the matching\nlibrary calls (`rebar.show_ticket(...)`), or the MCP read tools.\n\nFor reference, [`docs/sample-ticket-log.jsonl`](docs/sample-ticket-log.jsonl) is a\nsmall **synthetic** event log (one event per line) showing what the underlying\ndata actually looks like — a two-agent epic + child tickets exercising\ncreate/claim/comment/link/tag/file-impact/sign/transition. Note that its lines are\ndeliberately **not** in timestamp order: that is the point. The event body schema\nis documented in [`docs/event-schema.md`](docs/event-schema.md).\n\n## Python library\n\n```python\nimport rebar\n\nrebar.init_repo(repo_root=\"/path/to/repo\")\ntid = rebar.create_ticket(\"story\", \"Add login page\", priority=2)\nticket = rebar.show_ticket(tid)                 # TicketState\ntickets = rebar.list_tickets(status=\"open\")     # list[TicketState]\ntry:\n    rebar.transition(tid, \"open\", \"in_progress\")\nexcept rebar.ConcurrencyError:\n    ...                                          # ticket changed since last read\n\npreview = rebar.bridge_preview(only=[tid])       # typed, non-mutating Jira plan\nrun = rebar.bridge_run(profile=\"dry-run\")         # captured scheduled-run result; prints nothing\nsync = rebar.bridge_sync(max_changes=10)         # typed, explicitly mutating sync\nstatus = rebar.bridge_status(max_age_seconds=3600)\n# Durable operator controls and the live six-step capability check:\nrebar.bridge_pause(\"maintenance\")\nrebar.bridge_resume()\naccess = rebar.bridge_check_access()\n\n# Legacy reconcile(mode=...) is not a Python API; use explicit bridge operations.\naudit = rebar.bridge_fsck()                       # offline bridge audit\n\n# Sign a DSSE operation certificate by applying SSHSIG to its PAE bytes with the environment's Ed25519 key and principal.\nrebar.sign_manifest(tid, [\"unit tests: PASS\", \"security review: clean\"])\nverdict = rebar.verify_signature(tid)            # {\"verified\": True, \"verdict\": \"certified\", ...}\n\n# Native, in-process reads (no subprocess):\nfrom rebar import reduce_all_tickets, reduce_ticket\n```\n\n**Typed return contract.** The schema-backed `rebar.*` functions are annotated\nwith `TypedDict`s in [`rebar.types`](src/rebar/types.py) (e.g. `TicketState`,\n`TransitionResult`, `ClaimResult`), so a type checker knows which keys a return\nvalue carries. These are derived from the canonical JSON Schemas and describe the\n*guaranteed* keys — returns stay plain `dict`s and the runtime shape is open\n(extra keys may appear), so this is a floor, not a closed universe. Import them for\nannotations/`TypedDict` access:\n\n```python\nfrom rebar.types import TicketState, TransitionResult\n\nt: TransitionResult = rebar.transition(tid, \"open\", \"in_progress\")\n```\n\n**Stable exception surface.** `rebar.RebarError` (base) and its subclass\n`rebar.ConcurrencyError` are the public exceptions. `RebarError` carries\n`.returncode` (the underlying engine exit code) and `.stderr`; `ConcurrencyError`\n(exit 10) means a status-dependent op (`transition`/`claim`/`reopen`) lost an\noptimistic-concurrency race — re-read and retry, don't force. Catch `RebarError`\nto handle any rebar failure uniformly.\n\n**What's stable to depend on.** rebar is versioned 0.x; see\n[docs/api-stability.md](docs/api-stability.md) for the per-surface stability\nmatrix (CLI, `--output json` schemas, the `rebar.*` facade, MCP tools, the event\nwire format, and config keys) and what \"may change before 1.0\" means for each.\n\n## MCP server\n\n```bash\nrebar-mcp          # stdio transport\n```\n\nExposes ticket operations as MCP tools. The **complete tool reference**, grouped by\ngate tier (read-only / LLM-gated / write-gated), is\n[docs/mcp-reference.md](docs/mcp-reference.md) (generated from the server's own\nregistrars). Use the explicit `bridge_preview`, `bridge_run`, `bridge_sync`, `bridge_status`,\n`bridge_pause`, `bridge_resume`, `bridge_check_access`, and `bridge_fsck` tools.\nThe former MCP `reconcile(mode=...)` compatibility tool is no longer registered.\nRun/sync/pause/resume require `REBAR_MCP_ALLOW_JIRA_SYNC=1`;\n`REBAR_MCP_READONLY=1` blocks every mutation. To\nregister it in an MCP client (registry name\n`io.github.navapbc/rebar`, or a direct `uvx` config), see\n[Install → MCP server](#mcp-server--from-the-mcp-registry) above.\n\n**Maintainers:** the registry manifest lives in [`server.json`](server.json);\npublish/update it with the `mcp-publisher` CLI (see `docs/releasing.md`). The\nregistry verifies PyPI-package ownership via this annotation (kept in this\nREADME, which is the PyPI long description):\n\nmcp-name: io.github.navapbc/rebar\n\n## License\n\nApache-2.0 — see [`LICENSE`](LICENSE).\n\n## Configuration\n\nrebar reads **TOML** config from `[tool.rebar]` in `pyproject.toml` or a standalone\n`rebar.toml` (nearest up-tree, stopping at `.git`), falling back to a user config at\n`~/.config/rebar/config.toml` (honoring `$XDG_CONFIG_HOME`). Precedence, highest\nfirst: **`rebar -c SECTION.KEY=VALUE` / CLI flag > `REBAR_<SECTION>_<KEY>` env >\nproject config > user config > built-in default.** `rebar config` prints the resolved\nvalues and which layer each came from.\n\n```toml\n[tool.rebar]\nverify.require_completion_verification_for_close = true  # gate work-ticket close on a PASS\n                                           # completion verdict (signed onto the ticket);\n                                           # fail-closed. Default false.\nticket.display_mode = \"auto\"               # auto | canonical | alias | short\ncompact.threshold   = 10\nsync.push = \"always\"                       # always | async | off\nsync.pull = \"on\"                           # on | off\nmcp.readonly = false\nscratch.base_dir = \"\"                      # default <repo>/.rebar/scratch\ntracker.dir    = \".tickets-tracker\"        # store worktree/symlink dir (env REBAR_TRACKER_DIR)\ntracker.branch = \"tickets\"                 # orphan branch the event log lives on (env REBAR_TRACKER_BRANCH)\n```\n\nThe full key set, the `REBAR_<KEY>` env names, and deprecation aliases are in\n[`docs/config.md`](docs/config.md).\n\nWhen the close gate is enabled, a close transition runs the completion verifier against the selected code ref. A passing verifier records a DSSE operation-certificate envelope that carries an SSHSIG signature over its PAE bytes, produced with the signing environment's Ed25519 key. The certificate principal identifies that environment. Run the transition again if the verified code changes. `--force=<reason>` bypasses the gate and records no certificate.\n\nrebar keeps its writable state under `.rebar/` at the repo root. The `scratch`\nstore defaults to `<repo>/.rebar/scratch/` (override with `scratch.base_dir` /\n`REBAR_SCRATCH_BASE_DIR`), and one-shot migration stamps are written under\n`.rebar/` as well.\n\n## Tests\n\nRun the suite from an environment with the `[dev]` extra installed (a venv is\nrecommended); the interface-parity tests import the MCP server, so a bare\ninterpreter without the `mcp` extra will **error** rather than skip.\n\n```bash\nuv sync --extra dev && source .venv/bin/activate  # locked install: pytest, mcp, ruff, mypy\npytest -m \"not integration\"                   # the single entry point (CI runs this)\npytest tests/interfaces                       # interface-parity tier only\npytest tests/scripts                          # engine/reconciler tier only\n```\n\n**`pytest` is the single entry point.** The engine is pure in-process Python\n(the bash engine and its `.sh` suites were removed in the bash→Python migration —\nsee `docs/bash-migration.md`). CI (`.github/workflows/test.yml`) runs\n`pytest -m \"not integration\"` on\nUbuntu and macOS for every push and PR. The `integration` tier (live Jira /\nnetwork) is **excluded** from that default run; run it explicitly with credentials\nvia `pytest -m integration`.\n\nThe Python suite is sub-divided by concern:\n\n- `tests/scripts`, `tests/unit` — the in-process engine (reducer, graph, reconciler).\n- `tests/interfaces` — proves the **library, CLI, and MCP** interfaces behave\n  identically over one git-backed store:\n  - `test_parity.py` runs each operation through all three interfaces (and a\n    cross-interface coherence check: write via one, read via the others);\n  - `test_surface.py` pins the per-interface capability surface (e.g. MCP has no\n    `init`; there is no `classify`);\n  - `test_library.py` / `test_cli.py` / `test_mcp.py` cover per-interface\n    specifics (typed exceptions, exit-code passthrough, read-only/live gates).\n",
  "bytes": 36625,
  "sha": "cdd85df732190633cbb8092dd6db387a0db10e2a23ad9fa46c93425af1651671",
  "repo_slug": "navapbc/rebar",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_navapbc_rebar_aab44404/readme"
}