{"id":51167092,"url":"https://github.com/navapbc/rebar","last_synced_at":"2026-06-26T20:30:24.996Z","repository":{"id":366923786,"uuid":"1263103545","full_name":"navapbc/rebar","owner":"navapbc","description":"LLM-first ticket system with bidirectional Jira integration for human interface.","archived":false,"fork":false,"pushed_at":"2026-06-23T23:12:17.000Z","size":10878,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-23T23:16:55.454Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/navapbc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-08T16:14:51.000Z","updated_at":"2026-06-23T21:46:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/navapbc/rebar","commit_stats":null,"previous_names":["navapbc/rebar"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/navapbc/rebar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navapbc%2Frebar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navapbc%2Frebar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navapbc%2Frebar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navapbc%2Frebar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/navapbc","download_url":"https://codeload.github.com/navapbc/rebar/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navapbc%2Frebar/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34832916,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-26T20:30:23.971Z","updated_at":"2026-06-26T20:30:24.972Z","avatar_url":"https://github.com/navapbc.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rebar\n\nA git-native ticket system for coordinating coding agents — and the humans\nworking alongside them.\n\nPoint several agents at one repo and they immediately need a shared place to\ncoordinate: to claim work without grabbing the same ticket, record what they\ndiscover, and hand off cleanly — while your teammates stay in the loop through\nJira. rebar makes the tracker *part of the repo itself*, so it travels with every\nclone, needs no database or daemon, and lets many agents and sessions write at\nonce without merge conflicts or lost work.\n\nIt's an event-sourced ticket system with a Jira reconciler, exposed three ways:\n\n- **CLI** — the `rebar` command\n- **Python library** — `import rebar`\n- **MCP server** — `rebar-mcp` (stdio)\n\nTickets are stored as an append-only event log on a dedicated `tickets` git\norphan branch (worktree at `.tickets-tracker/`); state is computed by replaying\nevents. A level-triggered reconciler bidirectionally syncs tickets with Jira. Both\nthe branch name and the worktree/symlink dir default to those values but are\nconfigurable (`tracker.branch` / `tracker.dir`) — see [Configuration](#configuration).\n\nThis project was extracted from the `digital-service-orchestra` Claude Code\nplugin. It began as a bash + Python engine; that engine has since been fully ported\nto in-process Python (see `docs/bash-migration.md`). The reconciler ships under\n`src/rebar/_engine/` as package data, and the three interfaces are thin layers over\nthe in-process core.\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- **Quality gates** — clarity, acceptance-criteria, dispatch-readiness, and\n  repo-wide health checks keep work dispatch-ready.\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**Runtime (system):**\n- Python ≥ 3.11\n- `git` — required (the store is a git orphan branch + worktree). The engine is\n  pure in-process Python; `bash` and `jq` are **not** required at runtime.\n- `flock` from **util-linux** — recommended for robust write serialization, but\n  **not strictly required**: it is not on `PATH` by default on macOS\n  (`brew install util-linux`), and when no util-linux `flock` is found rebar falls\n  back to a `mkdir`-based lock automatically. (A non-util-linux `flock` such as\n  BusyBox's is ignored in favor of the fallback.)\n- `acli` (Atlassian CLI) — only for **live** Jira reconciliation.\n\n**Python dependencies — runtime (prod) vs. rebar development.** rebar keeps a\ndeliberately tiny footprint; the distinction is between *running* rebar (and its\noptional capabilities) and *developing rebar itself*:\n\n- **Runtime — base.** `pip install nava-rebar` gives the `rebar` CLI + `import\n  rebar` library + the **lean workflow engine** (author/validate/render/run\n  *scripted* workflows). Its only runtime dependencies are **`pyyaml`** (the\n  workflow DSL loader, epic a88f) and **`jsonschema`** (the schema-registry +\n  workflow input/output-contract validator) — both lean-runtime paths — so the\n  engine core and reconciler are otherwise stdlib-only.\n- **Runtime — optional capability extras** (install only what you use; each is\n  lazy-imported, so the base stays light and CI enforces that):\n  - **`[mcp]`** — the `rebar-mcp` server (`mcp\u003e=1.2`).\n  - **`[agents]`** — the LLM agent-operations framework + **agentic workflow\n    steps** (`rebar review`, the `code_review` workflow): the provider-agnostic\n    pydantic-ai runtime (`pydantic-ai-slim[anthropic]` + `json-repair`).\n  - **`[eval]`** — prompt evaluation (`rebar prompt eval`): Inspect AI. An\n    authoring/CI capability, not needed to serve.\n  - **`[tracing]`** — the OTLP trace sink (write-only; OpenTelemetry is never read\n    back into a rebar decision).\n- **Development (working ON rebar).** `pip install -e '.[dev]'` adds the\n  test/lint/type tooling (`pytest`, `ruff`, `mypy`, `hatchling`) and\n  self-references `[agents]` so the validation tests **run** rather than skip. It\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** are needed **only** for the workflow visual editor's front-end —\n    *rebuilding* its vendored bundle (`src/rebar/llm/workflow/editor_assets/`, the\n    bpmn-js editor) and running the faithful editor **E2E tier** (`tests/e2e/`, which\n    drives the real bpmn-io libraries). Both are developer-only: the built bundle is\n    committed/shipped, and the E2E tier self-skips when Node is absent, so neither the\n    base install nor the default test suite needs Node. See\n    [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` and `python3` (≥ 3.11); a\nutil-linux `flock` is used for write serialization when present, with a `mkdir`\nfallback otherwise; `acli` only for live Jira reconciliation.)\n\n### Homebrew (CLI)\n\n```bash\nbrew install navapbc/rebar/rebar\n# or: brew tap navapbc/rebar \u0026\u0026 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)\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[eval]'      # + prompt evaluation: `rebar prompt eval` (Inspect AI)\npip  install 'nava-rebar[tracing]'   # + OTLP trace sink (write-only)\npip  install 'nava-rebar[agents,eval,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` need `[agents]`. Authoring\na workflow **visually** — `rebar workflow edit \u003cfile\u003e`, 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). See\n[docs/workflow-editor.md](docs/workflow-editor.md).\n\nThe `[agents]` extra adds the optional **LLM agent-operations framework**\n(`rebar.llm`) — tool-using agents that review tickets/code and emit structured\nfindings, over library / CLI (`rebar review`) / MCP. It is multi-provider\n(**Claude** and **ChatGPT** out of the box, plus Gemini and OpenAI-compatible local\nservers like LMStudio/Ollama via `REBAR_LLM_MODEL`/`REBAR_LLM_MODEL_PROVIDER`/\n`REBAR_LLM_BASE_URL`) and is **never required by core rebar** — none of the LLM\nstack is installed or imported unless you opt into this extra (CI enforces it);\nsee [docs/llm-framework.md](docs/llm-framework.md).\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;\n`reconcile` is dry-run unless `REBAR_MCP_ALLOW_JIRA_SYNC=1` (deprecated alias\n`REBAR_MCP_ALLOW_RECONCILE_LIVE`). Both flags\naccept any case-insensitive truthy value — `1`, `true`, or `yes` (surrounding\nwhitespace tolerated); anything else (incl. unset) is off.\n\n### From source\n\n```bash\ngit clone https://github.com/navapbc/rebar \u0026\u0026 cd rebar\npip install .              # library + CLI (runtime deps: pyyaml, jsonschema)\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):\npip install -e '.[dev]'\n```\n\n\u003e **Packaging note — why rebar installs *unpacked* to disk.** The library, CLI,\n\u003e MCP server, and the whole read/write core run **in-process** in Python. The one\n\u003e component that runs as a subprocess is the Jira **reconciler**, which ships under\n\u003e `src/rebar/_engine/` as package **data** (`python -m rebar_reconciler`, plus the\n\u003e `jira-capability-probe.py` script and the alias wordlist): it is launched and\n\u003e read from the filesystem as real on-disk files, so the package must be installed\n\u003e unpacked to a real directory and **zipimport / zip-safe bundles (zipapp, shiv,\n\u003e PEX, Lambda zips) are unsupported**. Every standard install satisfies this:\n\u003e pip/pipx wheels (hatchling builds unpacked), editable installs, and Homebrew all\n\u003e land real files. `engine_dir()` asserts the engine dir is present on disk at the\n\u003e first reconciler call and fails loudly otherwise.\n\n## CLI\n\n```bash\nrebar init                                   # create the tickets branch + worktree\nrebar create story \"Add login page\"          # prints the ticket id\nrebar list [--status=open] [--has-tag=...]   # JSON array\nrebar show \u003cid|alias\u003e                         # compiled ticket state (JSON)\nrebar transition \u003cid\u003e \u003ccurrent\u003e \u003ctarget\u003e      # optimistic-concurrency status change\nrebar comment \u003cid\u003e \"\u003cbody\u003e\"\nrebar link \u003cid1\u003e \u003cid2\u003e \u003crelation\u003e            # relation REQUIRED (see relations below)\nrebar unlink \u003csource\u003e \u003ctarget\u003e               # remove ONE link for the ordered pair (no relation arg)\nrebar deps \u003cid\u003e                               # dependency graph\nrebar search \u003cquery\u003e                          # full-text over titles/descriptions/comments/tags (JSON)\nrebar ready                                   # tickets with all blockers closed\nrebar next-batch \u003cepic-id\u003e                    # unblocked tickets under an epic's hierarchy\nrebar validate                                # repo-wide tracker health (NO ticket id; whole-store score 1-5)\nrebar clarity-check \u003cid\u003e / check-ac \u003cid\u003e / quality-check \u003cid\u003e   # per-ticket quality gates\nrebar review-plan \u003cid\u003e                        # plan-review gate: DET floor + 3-pass advisory; signs an attestation (exit 0=PASS,1=BLOCK,2=INDETERMINATE)\nrebar sign \u003cid\u003e '[\"ran tests: PASS\", \"lint clean\"]'   # HMAC-sign a manifest of verified steps\nrebar verify-signature \u003cid\u003e                   # certify the steps match the signature (exit 0=certified)\nrebar export [-o FILE]                        # store -\u003e NDJSON (one ticket/line; for jq/DuckDB/pandas + rebar-\u003erebar migration)\nrebar import [FILE]                           # import export NDJSON (fresh local ids; [--dry-run])\nrebar reconcile [--mode dry-run|reconcile-check|live]   # Jira sync (default: dry-run)\n```\n\nRun `rebar help` (or `rebar --help` / `-h`) for the subcommand overview, and\n`rebar \u003csubcommand\u003e --help` (or `rebar help \u003csubcommand\u003e`) for a specific\nsubcommand's usage — `--help` prints usage and never executes the command.\nHelp is only recognized as the first argument after the subcommand, so a\n`--help`/`-h`/`help` that appears inside a free-text parameter (title, comment\nbody, search query, …) is treated as literal text, not a help request.\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**`validate` vs. the per-ticket gates.** `rebar validate` takes **no ticket id** —\nit scans the whole store and prints an overall tracker-health score (1-5, exit\n0-4) bucketed into critical / major / minor / warning findings (`--output json`,\n`--terse`, `--verbose`, `--fix`). Passing it a ticket id errors. The *per-ticket*\nquality gates are separate commands that each take an `\u003cid\u003e`: `clarity-check`,\n`check-ac`, `quality-check`. They are **structural floor checks** — they verify a\nticket is *shaped* like dispatchable work, not that the content is good. Every\ntype needs an `## Acceptance Criteria` checklist (`- [ ]` items); `check-ac` and\n`clarity-check` both require it. See the per-type ticket template (Why/What/Scope\nfor stories, Reproduction Steps for bugs, Success Criteria/Context for epics) in\n[CLAUDE.md](CLAUDE.md#ticket-template-the-gates-enforce).\n\n**Links.** `rebar link \u003cid1\u003e \u003cid2\u003e \u003crelation\u003e` **requires** a relation; the six\nrelations are `blocks`, `depends_on`, `relates_to`, `duplicates`, `supersedes`,\n`discovered_from`. `rebar unlink \u003csource\u003e \u003ctarget\u003e` takes **no** relation\nargument — it is pair-scoped and removes the **most-recently-created** link\nbetween that ordered pair, one per call, so to remove multiple links between the\nsame pair you call `unlink` repeatedly. Note that **blocking** links\n(`blocks`/`depends_on`) may be promoted up the parent hierarchy when created (see\nbelow), so `unlink` must target the **promoted (ancestor)** endpoint to remove\nsuch a link.\n\n### Signing a manifest of verified steps\n\n`rebar sign \u003cid\u003e \u003cmanifest\u003e` records a **cryptographic attestation** on a ticket:\na manifest (a JSON array of verified-step strings) plus an HMAC-SHA256 signature\ncomputed with a key that is **specific to the environment** rebar runs in. The key\nis resolved from `REBAR_SIGNING_KEY` (injected out-of-band into a shared\ndeployment — e.g. an MCP server) or, failing that, a per-environment\n`.signing-key` file generated on first use (gitignored, never committed, never\nshared). `rebar verify-signature \u003cid\u003e` recomputes the HMAC with the local key and\n**certifies** that the recorded steps still match the signature:\n\n```bash\nrebar sign abcd-1234 '[\"unit tests: PASS\", \"security review: clean\", \"deployed to staging\"]'\nrebar verify-signature abcd-1234        # SIGNATURE: certified — verified steps match the signature\n```\n\nThe signature binds both the ticket id and the manifest, so it cannot be replayed\nonto another ticket and any edit to the step list invalidates it. Because the key\nnever leaves the environment, `verify-signature` reports `foreign_key` (rather\nthan `certified`) when a record was signed by a *different* environment — only the\nenvironment that holds the key can certify its own attestations. The signature is\nstored as a normal append-only `SIGNATURE` event, so it replays into `show`\noutput, survives compaction, and flows to other clones like any other write.\n\nThe signing key is a shared secret (HMAC), so the attestation proves a signature\nwas produced by a holder of the environment key and that the steps are unaltered\nsince — it is **not** a public-key identity. Anyone who can read the\n`.signing-key` file (written `0600`, owner-only) or the injected `REBAR_SIGNING_KEY`\ncan forge a `certified` record, so protect read access to the environment\naccordingly.\n\n### Hierarchy promotion of blocking links\n\nFor **blocking** dependencies only (`blocks`, `depends_on`), rebar promotes the\nlink endpoints up the parent hierarchy so the dependency sits between tickets at\na comparable level (epic↔epic, story↔story, task/bug↔task/bug). When it does so\nit emits a `REDIRECT: A→B promoted to …` note. Non-blocking relations\n(`relates_to`, `duplicates`, `supersedes`, `discovered_from`) are linked exactly\nas 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### 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.)\n\n**Opt out** for a pure-local replay (offline work, tight loops, or right after a\nwrite that already synced): set `REBAR_SYNC_PULL=off` (honored everywhere;\ndeprecated alias `REBAR_NO_SYNC=1`) or pass `--no-pull` to any read subcommand\n(e.g. `rebar list --no-pull`; deprecated alias `--no-sync`). Only the network\nfetch/merge is skipped; the local reduce/cache path is unchanged. 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\u003cid\u003e`, `rebar list`, `rebar deps \u003cid\u003e`, `rebar search \u003cquery\u003e` (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)                 # dict\ntickets = rebar.list_tickets(status=\"open\")     # list[dict]\ntry:\n    rebar.transition(tid, \"open\", \"in_progress\")\nexcept rebar.ConcurrencyError:\n    ...                                          # ticket changed since last read\n\nresult = rebar.reconcile(\"dry-run\")              # Jira sync (non-mutating)\n\n# Cryptographic attestation (environment-bound HMAC):\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## MCP server\n\n```bash\nrebar-mcp          # stdio transport\n```\n\nExposes ticket operations as MCP tools. `reconcile` defaults to `dry-run`\n(`live` requires `REBAR_MCP_ALLOW_JIRA_SYNC=1`, deprecated alias\n`REBAR_MCP_ALLOW_RECONCILE_LIVE`). Set `REBAR_MCP_READONLY=1`\nto expose only the read tools (no write/mutation tools). To register it in an MCP\nclient (registry name `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 \u003e `REBAR_\u003cSECTION\u003e_\u003cKEY\u003e` env \u003e\nproject config \u003e user config \u003e built-in default.** `rebar config` prints the resolved\nvalues and which layer each came from.\n\n```toml\n[tool.rebar]\nverify.require_signature_for_close = true  # gate story/epic close on a certified\n                                           # signature at HEAD (rebar sign); default\n                                           # false. Alias: verify.require_verdict_for_close\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 \u003crepo\u003e/.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_\u003cKEY\u003e` env names, and deprecation aliases are in\n[`docs/config.md`](docs/config.md). The legacy flat `.rebar/config.conf`\n(`key=value`) is still read during a deprecation window for back-compat.\n\nWhen the close gate is enabled, closing a story/epic requires a **certified\nsignature made at the current HEAD** — sign a manifest of verified steps\n(`rebar sign \u003cid\u003e '[...]'`) then `rebar transition \u003cid\u003e closed`; re-sign if HEAD\nmoved, or bypass with `--force-close=\u003creason\u003e`. This replaces the older\n`--verdict-hash`/`compute-verdict-hash.sh` gate, which is now deprecated.\n\nrebar keeps its writable state under `.rebar/` at the repo root. The `scratch`\nstore defaults to `\u003crepo\u003e/.rebar/scratch/` (override with `scratch.base_dir` /\n`REBAR_SCRATCH_BASE_DIR`; the unprefixed `SCRATCH_BASE_DIR` is a deprecated alias),\nand one-shot migration stamps are written under `.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\npython -m venv .venv \u0026\u0026 source .venv/bin/activate\npip install -e '.[dev]'                       # editable + 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","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnavapbc%2Frebar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnavapbc%2Frebar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnavapbc%2Frebar/lists"}