# emBEADings: find duplicate and related issues in Beads and Linear emBEADings reads your [Beads](https://github.com/gastownhall/beads) tracker (or one Linear team) and finds the issues that belong together. It only reads: it never changes a status, adds a link or posts a comment, and issue text is embedded on your own machine. Install: `pipx install embeadings` (or `pip install embeadings`, Python 3.11+). Source: https://github.com/CantrellJax/embeadings (MIT). Package: https://pypi.org/project/embeadings/ ## What it finds - **Issues that belong together but have no link.** `embead neighbors ISSUE --orphans-only` lists the issues most like one issue that the tracker does not link to it. `embead triage` gives a short, bounded list of such pairs across the whole tracker. - **Open issues a merge already did.** `embead superseded --since 2026-10-01` reads the merged changes from git (or a list of pull requests), lists the open issues a change names by ID, and ranks the rest against each squashed commit. - **Work about to collide.** `embead collisions` finds active issues that touch the same files, from explicit references or real git worktree diffs. - **Prose that says "duplicate of" with no link.** `embead mentions` finds issues whose notes say "folded into", "duplicate of" or "superseded by" another, where no typed link backs it. - **Duplicates before they are filed.** `embead match --title "..."` finds the existing issues a new one would duplicate, including closed ones. - **A whole sprint at once.** `embead neighbors --ids-file sprint.txt --format table` prints one line per lead across many seeds, with its best seed, assignee and guards (owner labels, in-progress work). Every report is advisory evidence with a versioned JSON schema (`embead schema REPORT_TYPE`), never a verdict. ## What the page shows The page is a scroll story told with beads. Each bead is an invented issue: colour is topic, material is status (clear glass for open, lit from inside for in progress, matte for blocked, frosted for closed). 1. A pile of beads rests on the word emBEADings. 2. Sorted: scrolling strings the beads onto one cord per topic, the way emBEADings groups similar issues. 3. Tied: three beads hang off their cord with no link. A pink arc ties each to the issue it matches, 0.91, 0.87 and 0.84 alike. 4. Shipped: merged pull request #212 carries off the three issues it already did. An issue labelled owner-run stays put. Below the story, visitors drag merged pull request cards onto a table of beads; each card strings up the issues it finished. ## Who made it Jackson Cantrell (https://jacksoncantrell.com, https://github.com/CantrellJax, https://linkedin.com/in/jacksoncantrell) makes products, and these days makes them alongside a lot of AI agents. emBEADings is the tool he built to keep his own Beads tracker honest. Issues, ideas and pull requests are welcome: https://github.com/CantrellJax/embeadings/issues/new ## Guides - [Find duplicate issues in Beads, even in different words](https://embeadings.jacksoncantrell.com/beads-duplicate-issues/) - [Clean up a bloated Linear backlog](https://embeadings.jacksoncantrell.com/linear-backlog-cleanup/) - [Which open issues did my merged pull requests already fix?](https://embeadings.jacksoncantrell.com/close-issues-after-merge/) - [Teach your agents to look before they file](https://embeadings.jacksoncantrell.com/for-agents/) (raw instructions: https://embeadings.jacksoncantrell.com/agents.md) emBEADings is an independent tool listed in the Beads community tools: https://beads.gascity.com/community-tools --- # Duplicate issues in Beads? Find them by meaning. Find duplicate and related issues in a Beads tracker, including ones written in different words, and the ones no link connects. emBEADings is a free, read-only, local tool listed in the Beads community tools. ## Why it happens Agents and people file issues fast. Two of them end up describing the same work in different words, nothing links them, and two agents pick them up. Beads helps once you know: `bd duplicates` finds identical copies, `bd find-duplicates` compares wording (or asks an LLM if you give it an API key), and `bd duplicate A --of B` closes the copy with a link. ## What emBEADings adds It embeds every issue with a small model on your machine, so no API key and nothing leaves your laptop. It shows the pairs that mean the same thing even when the words differ, skips pairs your tracker already links, and gives each lead its evidence. It only reads; you decide. ## Try it ```sh pipx install embeadings cd your-project # any folder where bd works embead triage # a short list of pairs worth a look embead neighbors bd-a1b2 --orphans-only # issues like this one, with no link to it embead mentions # notes that say "duplicate of" with no link behind them bd duplicate bd-a1b2 --of bd-c3d4 # then close the real copy, in Beads ``` ## Questions ### How is this different from bd find-duplicates? bd find-duplicates scores word overlap, or sends pairs to an LLM with your API key. emBEADings compares meaning with a local embedding model, leaves out pairs that are already linked, and also checks merged pull requests and active worktrees. They work well together. ### Does emBEADings change my Beads database? No. It runs bd in read-only mode and has no write commands. You mark duplicates yourself with bd duplicate. ### Is emBEADings part of Beads? It is an independent open-source tool, listed in the Beads community tools. emBEADings is free and open source (MIT): https://github.com/CantrellJax/embeadings. Made by Jackson Cantrell (https://jacksoncantrell.com). Listed in the Beads community tools: https://beads.gascity.com/community-tools --- # A bloated Linear backlog is mostly the same issue, filed again. A free, read-only tool that reads one Linear team, finds duplicate and related issues by meaning, and lists the open issues your merged pull requests already finished. Issue text is embedded on your machine. ## Why it happens A backlog grows one reasonable issue at a time: a bug reported twice in different words, a follow-up nobody linked, an issue a pull request finished last month. Linear can mark a duplicate once you know, but finding them across hundreds of issues is the hard part. ## What emBEADings does It reads one Linear team through the API, embeds the issue text on your machine, and gives you a short, ranked list: issues that mean the same thing, related issues with no link, and open issues your merged pull requests already did. It never writes to Linear. ## Try it ```sh pipx install embeadings export LINEAR_API_KEY=lin_api_... # a personal API key embead --source linear --linear-team ENG triage embead --source linear --linear-team ENG neighbors ENG-123 --orphans-only embead --source linear --linear-team ENG superseded --since 2026-10-01 --repo ~/code/app ``` ## Questions ### Does it change anything in Linear? No. The Linear adapter only reads. You mark duplicates and close issues in Linear yourself. ### Do my issues get sent to an AI service? No. Issue text is embedded locally with a small open model. Only the Linear API itself is called. ### Can it tell me which issues a merged pull request already fixed? Yes. embead superseded reads your merged commits from git and lists open issues they name by ID, then ranks the rest by how closely they match each commit. emBEADings is free and open source (MIT): https://github.com/CantrellJax/embeadings. Made by Jackson Cantrell (https://jacksoncantrell.com). Listed in the Beads community tools: https://beads.gascity.com/community-tools --- # Your pull request merged. The issue is still open. After a sprint, list the open issues your merged pull requests already finished: issues a change names by ID, and issues that match a squashed commit. Read-only, for Beads and Linear. ## Why it happens After a busy day of merges, the tracker is full of issues whose work landed under another name: the follow-up a review fix already covered, the bug a refactor made moot, the in-progress issue whose pull request merged an hour ago. ## What emBEADings does `embead superseded` reads the changes merged since a date or commit, straight from git, or from a list of pull requests. It lists open issues a change names by ID, then ranks the rest against each commit, splitting squash merges back into their commits so a fix bundled into a bigger change still finds its issue. Issues with an owner label or someone working on them are flagged, never touched. ## Try it ```sh embead superseded --since 2026-10-01 # merges on this branch since a date embead superseded --since main~30 --json # or since a commit # or from GitHub pull requests gh pr list --state merged --json number,title,body,files,mergedAt | jq -c '.[]' > prs.jsonl embead superseded --since 2026-10-01 --changes-file prs.jsonl ``` ## Questions ### Does it close the issues for me? No. It lists them with the change that is the evidence. You close them in your tracker. ### Does it work with Linear as well as Beads? Yes. Add --source linear --linear-team ENG and it matches your merges against that team's issues. ### What about squash merges that bundle several fixes? Each squashed commit is ranked on its own, so a fix inside a larger pull request still points at the issue it closes. emBEADings is free and open source (MIT): https://github.com/CantrellJax/embeadings. Made by Jackson Cantrell (https://jacksoncantrell.com). Listed in the Beads community tools: https://beads.gascity.com/community-tools --- # Teach your agents to look before they file. Copy-paste instructions for AGENTS.md or CLAUDE.md that make coding agents check for an existing issue before filing a new one, look for related work before starting, and report what a merge already finished. ## Why it happens Coding agents are quick to open issues and slow to search. Each one sees only its own task, so the same bug gets filed three times in three wordings, and finished work stays open. ## What to add Paste the block below into your project's `AGENTS.md` or `CLAUDE.md`. It is also served as plain Markdown at /agents.md, so an agent can fetch it directly. emBEADings only reads, so these checks are safe to run on every task. ## Try it ```markdown ## Duplicate and stale issue checks (emBEADings) This project uses emBEADings (`embead`), a read-only tool that finds related issues. - Before filing an issue, run: embead match --title "" --body "<one-line summary>" If a result describes the same work, update that issue instead of filing a new one. - Before starting an issue, run: embead neighbors <issue-id> --orphans-only Related work with no link may already be in progress. Link it or ask. - After merging, run: embead superseded --since <date or commit> Tell the human which open issues look finished. Never close issues labelled owner-run, or in progress under someone else. - emBEADings never changes the tracker. Make changes with bd (or in Linear) yourself. ``` ## Questions ### Is there a plugin for Claude Code or Codex? Yes. The emBEADings repository includes a read-only agent plugin with triage, collisions and evaluate skills that call the installed embead CLI. ### Will these checks slow my agents down? Not much. Each check reads the tracker and runs a small local model; on a tracker of about 2,000 issues it takes under half a minute, and vectors are cached between runs. ### Can an agent fetch the instructions itself? Yes. They are plain Markdown at https://embeadings.jacksoncantrell.com/agents.md. emBEADings is free and open source (MIT): https://github.com/CantrellJax/embeadings. Made by Jackson Cantrell (https://jacksoncantrell.com). Listed in the Beads community tools: https://beads.gascity.com/community-tools --- # Project README (github.com/CantrellJax/embeadings) Find engineering work that may trip over the same code—without changing the tracker or sending issue text to an embedding API. emBEADings is a read-only coordination CLI for [Beads](https://github.com/gastownhall/beads) and Linear. It combines typed tracker relationships, local semantic retrieval, explicit code pointers, and genuine Git worktree changes into a bounded, deterministic review queue. Dependencies answer “what blocks this?” emBEADings asks “what else should I inspect before these changes merge?” It provides evidence, not an automatic verdict. > **Status:** v0.4 technical preview. The CLI and GitHub release are public; the bundled Codex and > Claude Code plugin is still a local developer preview. ## Quick start Python 3.11 or later is required. The default Beads source also requires an installed `bd` CLI. ```bash pipx install embeadings # or: uv tool install embeadings # Check the environment without loading issue text embead doctor # Produce a bounded coordination packet embead triage # Find active work touching the same files or modules embead collisions # Inspect semantic neighbors for one record embead neighbors ISSUE_ID --include-closed # Find the stragglers: similar records with no structural link embead neighbors ISSUE_ID --orphans-only # Several seeds in one load; rank and reverse rank beat raw scores embead neighbors ISSUE_A ISSUE_B --ids-file more-seeds.txt # Records whose text says "absorbed by X" or "duplicate of X" with no typed link embead mentions # Live issues whose parent is closed or missing (structural, no model) embead orphans # Nearest existing records for text that is not a bead yet (no placeholder record) embead match --title "Persist login across restarts" --body-file draft.txt # Live records that today's merges may already have done (reads git, never writes) embead superseded --since 2026-10-07 # One deduplicated line per neighbor across many seeds, with owner and in-progress guards embead neighbors --ids-file sprint.txt --exclude-siblings --respect-soft-links --format table ``` Without `pipx` or `uv`, the standard library is enough: ```bash python3 -m venv .venv . .venv/bin/activate # Windows PowerShell: .venv\Scripts\Activate.ps1 python -m pip install embeadings ``` For an immutable GitHub fallback, install the verified release wheel directly: ```bash python -m pip install \ "https://github.com/CantrellJax/embeadings/releases/download/v0.5.0/embeadings-0.5.0-py3-none-any.whl" ``` The first semantic command downloads the pinned [`minishlab/potion-base-8M`](https://huggingface.co/minishlab/potion-base-8M) model. Embedding happens locally. `collisions` does not load an embedding model. Release assets include a source archive and `SHA256SUMS`. See the [v0.5.0 release](https://github.com/CantrellJax/embeadings/releases/tag/v0.5.0) for versioned artifacts and checksums. ## What a lead looks like ![Synthetic terminal example of an observed exact-file collision](https://raw.githubusercontent.com/CantrellJax/embeadings/v0.5.0/assets/brand/synthetic-collision-evidence.svg) This shortened example is derived from the committed synthetic collision fixture: ```json { "issue_id": "demo-1", "related_issue_id": "demo-2", "kind": "exact-file", "confidence": "observed", "shared_paths": ["src/cache/index.py"], "evidence_sources": ["active-worktree-diff"], "what_to_verify": "Verify whether concurrent work will modify the shared file paths before implementation or merge." } ``` The full report also records repository provenance, revision relation, hub suppression, warnings, and the read-only policy. It contains pointers rather than source snippets. See [`examples/collisions.json`](https://github.com/CantrellJax/embeadings/blob/v0.5.0/examples/collisions.json) and the [example guide](https://github.com/CantrellJax/embeadings/blob/v0.5.0/examples/README.md). ## Why trust it? | Evidence | Result | Boundary | | --- | --- | --- | | Concurrent-worktree release gate | Recovered all 3 known exact-file collisions across 4 genuine active worktrees | One repository; not universal recall | | Dogfooding | Found 2 real association/scope defects before v0.4.0 | Demonstrates workflow value, not broad precision | | Ruff scale surrogate | 17/20 top-packet pairs were at least contextually useful across 8,143 public issues | Converted GitHub corpus; no native Beads graph or worktrees | | Release validation | Full CI passed across Linux, macOS, Windows, Python 3.11 and 3.14; wheel/sdist checksums and provenance published | Supply-chain and test evidence, not semantic quality | | Repeatability | Evaluation outputs were byte-stable and non-mutating | Determinism does not make a weak lead correct | Read the [dogfood release-gate story](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/articles/dogfooding-v040-worktree-gate.md), [aggregate v0.4.0 worktree gate](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/research/code-surface-v040-release-gate.md), [Ruff scale review](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/research/ruff-scale-surrogate-01.md), and the [research index](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/research/README.md) for methods, failure patterns, and limitations. ## How it works ```text Beads or one Linear team │ ├── typed relationships and lifecycle ├── explicit paths and observed worktree changes └── local whole-record and field-level embeddings │ ▼ bounded candidate union │ ▼ evidence receipts + review packet ``` `triage` is the opinionated front door. It admits at most 20 semantic candidates by default, includes code-surface analysis when genuine local Git evidence exists, and writes a complete audit report to external user state. Use `sweep` for experimental policy controls and `neighbors` for one-record inspection. The default is a reviewer-capacity budget, not corpus coverage; see the [review-budget decision](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/decisions/review-budget-default.md). For `triage`, `sweep`, and `batch`, use `--output-dir DIRECTORY` when you want the complete JSON, Markdown, and per-batch artifact set. Use `--output report.json` or `--output report.md` for one primary report file; the extension chooses the file format independently of `--json` stdout. Any other `--output PATH` remains a backward-compatible directory spelling. `neighbors`, `collisions`, `mentions`, `orphans`, `match`, and `superseded` always treat `--output` as one atomic report file. `collisions` reviews `open`, `in_progress`, and `blocked` work by default. It associates Git worktrees when a branch spells exactly one issue ID: the full ID, a prefix-less short form such as `abc12.4` or `abc12-4`, or an unambiguous `bead-N` suffix. Teach it another convention, or explicitly map an otherwise unassociated worktree: ```bash embead collisions --branch-pattern 'ticket/(?P<id>[a-z0-9.]+)' embead collisions --worktree-map embead-42=../feature-worktree ``` On a busy tracker most leads are `explicit` (two records mention the same path in prose). Keep only leads with worktree evidence with `--min-confidence corroborated`, or both sides observed with `--min-confidence observed`. Never fabricate a mapping: observed evidence must describe genuine active implementation work. Shared paths are coordination evidence, not proof that two tasks conflict. ## Linear Create a personal API key in Linear's Security & access settings and load it without placing the value directly in a shell-history entry: ```bash LINEAR_API_KEY="$(python -c 'import getpass; print(getpass.getpass("Linear API key: "))')" export LINEAR_API_KEY embead --source linear --linear-team ENG triage embead --source linear --linear-team ENG collisions ``` `LINEAR_ACCESS_TOKEN` accepts an OAuth token instead; set only one credential. The CLI queries one selected team through Linear GraphQL and does not reuse credentials held by an MCP or agent host. See the [Linear adapter contract](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/linear.md). ## Companion: emBEADify emBEADings finds the leads and never writes. When a human or coordinator has reviewed them and wants to act, [emBEADify](https://github.com/CantrellJax/embeadify) is the write-side companion: it turns reviewed decisions into parallel, dry-run-by-default, undoable Beads updates, and it can draft a commented decisions file from an `embead` report. Keeping the two apart is deliberate, so emBEADings stays read-only. ## Privacy and data boundary - Tracker adapters contain no mutation operations. - The default model embeds issue text locally; issue text is not sent to Hugging Face. - Linear mode sends tracker queries only to Linear itself. - Models and vectors use the platform user cache; reports use the platform user state directory. - Neither cache nor reports are written into the analyzed repository by default. - Collision reports contain code pointers, not source snippets. - A human or coordinator must verify every lead before changing tracker or source state. The first model download is network activity. Prepare it before loading private issues when evaluating under OS-level network denial. See the [safe offline evaluation guide](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/evaluation.md). ## Good fit / poor fit emBEADings is most useful when a tracker is too large for repeated full-context review, several people or agents work concurrently, and the team values a reproducible offline shortlist. It is less useful for a small tracker that one reviewer can read directly, repositories without meaningful tracker-to-code evidence, or teams seeking automatic issue mutation, orchestration, a dashboard, or a general memory system. Typed dependencies remain tracker truth; semantics complement them rather than re-deriving authority. ## Development Every clone or Git worktree must own its virtual environment: ```bash python3 scripts/worktree_env.py . .venv/bin/activate # Windows PowerShell: .venv\Scripts\Activate.ps1 python scripts/validate.py ``` The bootstrap refuses to reuse an active environment from another checkout. Validation checks the editable `embead` import target before formatting, lint, tests, and release checks. Read [`CONTRIBUTING.md`](https://github.com/CantrellJax/embeadings/blob/v0.5.0/CONTRIBUTING.md) before submitting fixtures or reports; private tracker content must never be committed. ## Agent plugin preview [`plugins/embeadings`](https://github.com/CantrellJax/embeadings/blob/v0.5.0/plugins/embeadings/README.md) packages `triage`, `collisions`, and `evaluate` skills for local Codex and Claude Code development. It delegates to the installed CLI, forces schema-v1 JSON, and verifies the read-only policy. It is not yet a marketplace release and grants no tracker-write authority. ## Documentation - [Documentation index](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/README.md) - [CLI and product specification](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/spec.md) - [Consumer and schema contract](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/consumer-contract.md) - [Performance and scale evaluation](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/performance.md) - [Research and evaluation ledger](https://github.com/CantrellJax/embeadings/blob/v0.5.0/docs/research/README.md) - [Versioned JSON Schemas](https://github.com/CantrellJax/embeadings/tree/v0.5.0/schemas/v1) and [synthetic examples](https://github.com/CantrellJax/embeadings/blob/v0.5.0/examples/README.md) ## Principles - **Read-only means read-only.** Analysis never closes, edits, labels, or reprioritizes work. - **The tracker remains authoritative.** Structure and lifecycle stay tracker data. - **Local-first and private.** The default semantic provider sends no issue content to a network API. - **Bounded and auditable.** A stable receipt explains what entered or was omitted from the queue. - **Agent-neutral.** Core analysis does not depend on Codex, Claude Code, Cursor, or another runtime. MIT licensed. emBEADings is not affiliated with Beads or Linear.