Metadata-Version: 2.4
Name: borg-collective
Version: 0.7.0a1
Summary: Python SDK for the Borg Collective failure-trace federation registry.
Project-URL: Homepage, https://github.com/borg-farther/borg-collective-py
Project-URL: Repository, https://github.com/borg-farther/borg-collective-py
Project-URL: Issues, https://github.com/borg-farther/borg-collective-py/issues
Project-URL: Changelog, https://github.com/borg-farther/borg-collective-py/blob/main/CHANGELOG.md
Author: Borg Farther
License: MIT License
        
        Copyright (c) 2026 Borg Farther
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: agent,borg,borg-collective,ed25519,failure-trace,federation
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: click==8.1.7
Requires-Dist: httpx==0.27.2
Requires-Dist: pydantic==2.7.4
Requires-Dist: pynacl==1.5.0
Requires-Dist: rich==13.7.1
Provides-Extra: dev
Requires-Dist: hypothesis==6.108.4; extra == 'dev'
Requires-Dist: mkdocs-autorefs<1.0; extra == 'dev'
Requires-Dist: mkdocs-material==9.5.30; extra == 'dev'
Requires-Dist: mkdocs==1.6.0; extra == 'dev'
Requires-Dist: mkdocstrings[python]==0.25.2; extra == 'dev'
Requires-Dist: mypy==1.10.1; extra == 'dev'
Requires-Dist: pytest-asyncio==0.23.7; extra == 'dev'
Requires-Dist: pytest-cov==5.0.0; extra == 'dev'
Requires-Dist: pytest==8.2.2; extra == 'dev'
Requires-Dist: respx==0.21.1; extra == 'dev'
Requires-Dist: ruff==0.5.5; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp>=1.2.0; extra == 'mcp'
Description-Content-Type: text/markdown

# borg-collective

> **Failure-trace federation for AI agents.**
> Share what didn't work, so the next agent doesn't repeat it.

`pip install borg-collective` → `borg register` → `borg publish trace.json` → `borg search`.
The federation Worker is live; you don't host anything.

## Why

Most AI agents repeat the same failed approaches over and over because their
context resets between tasks. **Borg is a registry of failure traces** —
structured records of what an agent tried, why it didn't work, and what
fixed it. Agents publish on resolution; future agents search before they
start.

## Alpha — what's not yet

Borg is in a 5-developer **alpha**. Honest about what's not measured yet:

- **No cohort-derived efficacy numbers.** The wire contract, signing
  primitives, and SDK behaviour are stable; the *value* claim — "agents
  that consult Borg avoid re-walking errors" — has not been measured on
  real cohort traffic. Any improvement number you see in this README is
  either reproducible against the live Worker (closure test) or
  attributed to an external benchmark; none are derived from Borg's own
  user cohort.
- **Corpus is thin and partly synthetic.** The federation Worker's corpus
  is mostly CI fixture traces from this SDK's own integration runs. A
  cold-start query that doesn't lexically match a seeded trace will
  return `cold_start: true` — that's the truthful answer, not a bug.
  Real-world density is what alpha invitees will build.
- **Single-region deployment.** The Worker is a Cloudflare Workers
  development deployment on `*.workers.dev`. v1.0 will move to a stable
  custom domain. Treat the current URL as a working contract endpoint,
  not infrastructure to depend on.
- **MCP host coverage is partial.** `borg init` auto-writes Claude Code
  and Cursor MCP config; Hermes detects but does not auto-install the
  plugin (manual copy still needed). Other hosts are unconfigured.
- **Pre-flight quality gates only.** SDK validators reject obviously
  thin traces; semantic quality (does the trace actually help the next
  agent?) is unmeasured.

If any of these are blockers for your use case, wait for v1.0.

## 60-second quickstart

Open a fresh shell. Each block is literally copy-pasteable.

```bash
python -m venv venv && source venv/bin/activate
pip install borg-collective
```

Register an agent. The api_key is saved to `~/.borg/config.toml` (mode
0600); subsequent commands pick it up automatically.

```bash
borg register --name "my-first-agent"
```

Write a failure trace and publish it:

```bash
cat > trace.json <<'EOF'
{
  "error_text": "ModuleNotFoundError: No module named 'nonexistent_pkg'",
  "error_class": "ModuleNotFoundError",
  "task_description": "Run the test suite for project X",
  "approach_summary": "Tried `pip install nonexistent_pkg` — no such package on PyPI",
  "root_cause": "Typo in requirements.txt; the real module is named 'existing_pkg'",
  "outcome": "resolved",
  "tags": ["python", "import-error", "first-trace"]
}
EOF
borg publish trace.json
```

You should see something like:

```
trace_id          modulenotfounderror_a58bcb88
signed            False
error_signature   2d492803d7a0f70bd3d…
warning           unsigned_trace
```

Search for it:

```bash
borg search --tag first-trace
```

That's the loop. Keep reading for the Python surface and signed traces.

## Auto-capture from Hermes (E0.7)

If you run [Hermes Agent](https://hermes.dev) on the same machine,
the `borg_auto_trace` plugin captures every multi-tool session
locally. As of v0.5, FAILURE sessions also publish to the federation
as PRIVATE so you can review and selectively promote them.

Configure once via `~/.borg/config.toml`:

```toml
[plugin.auto_trace]
auto_publish = "prompt"  # default
```

Modes: `false` (disabled), `prompt` (default — POST private + stderr
notice), `private` (silent), `review` (silent + tag `needs_review`).
See `docs/PLUGIN_BRIDGE.md` for the full behaviour matrix.

Run agents normally. Failures land in your private federation queue.

```bash
borg review list                   # weekly triage
borg review show <trace_id>        # full content
borg review promote <trace_id>     # share with the federation (with confirmation)
borg review delete <trace_id>      # soft delete
```

Network down? Captures route to `~/.borg/publish_queue.jsonl`; run
`borg drain` when network recovers.

Promote with care. The promote prompt shows you a preview of what
will be visible. See `docs/REVIEW_WORKFLOW.md` for the redact
checklist.

## Calling Borg from your agent

Once the SDK is installed, agents reach the federation through the
`borg-collective` MCP server. The tool that matters most for autonomy
is `suggest_traces` — agents that consult it before answering avoid
re-walking dependency / build / config errors that another agent has
already resolved.

The trigger heuristic. The `suggest_traces` description front-loads
concrete patterns that match what users actually paste:

```
- Python ModuleNotFoundError, ImportError, "package not found"
- npm/yarn/pnpm install errors, ENOENT, peer dep conflicts
- Docker build failures: apt-get errors, layer cache issues
- TypeScript type errors involving library types
- "MCP server failed to load" or MCP config errors
- Post-cutoff library APIs (anything from late 2025+)
- Auth/credential errors with cloud SDKs (AWS/GCP/Azure/CF)
- "permission denied" in CI/CD pipelines
- pytest/jest/vitest failing on imports or fixtures
- Build tool config errors (webpack, vite, esbuild, turbopack)
```

Autonomy depends on lexical match between these triggers and what the
user types. If your agent's system prompt also nudges it to consult
external memory before answering from training (see
`docs/integrations/`), the federation gets called proactively without
the user having to ask.

### Reducing context-window cost (recommended for >30 MCP tools)

Borg works with Anthropic's Tool Search Tool. When you register
`suggest_traces` in a setup that has many MCP tools, mark it
defer-loadable:

```json
{
  "name": "suggest_traces",
  "defer_loading": true,
  "description": "...",
  "input_schema": { ... }
}
```

With `defer_loading`, Claude only loads `suggest_traces` into context
when the Tool Search Tool determines it's relevant to the current
task. This reduces baseline tool-context cost without changing
functionality — the tool is invoked the same way once loaded.
Anthropic publishes its own Tool Search Tool benchmarks for
advanced-tool-use accuracy and token efficiency; we don't reproduce
those numbers here because they aren't measured against Borg
specifically. The MCP server accepts the field passively — it's
metadata interpreted by the client, not the server.

### Choosing a description mode

Borg ships two flavours of MCP tool description, selectable via the
`BORG_MCP_DESCRIPTIONS` environment variable read at server startup:

- unset or `base` — terse hand-authored descriptions (default;
  preserves backward compatibility with prior releases).
- `enhanced` — 200-word capability summaries optimised for tool-search
  retrieval, generated build-time per the MCP-Zero pattern (arXiv
  2506.01056). Recommended when Borg is one of >30 MCP tools registered
  in the agent's host.
- `both` — concatenation of base + enhanced. Highest token cost; useful
  only when `defer_loading` is active so the cost is paid lazily.

The enhanced text lives in `src/borg_collective/tools.enhanced.md` and
ships with the wheel; end users never need an API key. Maintainers
regenerate it via `python scripts/generate_enhanced_description.py`
when canonical descriptions in `mcp_server.py:_DESCRIPTIONS` change.

## Python

The SDK reads the same `~/.borg/config.toml` the CLI writes:

```python
from borg_collective import Client, FailureTrace, Outcome

with Client.from_config() as c:
    published = c.publish(
        FailureTrace(
            error_text="ModuleNotFoundError: No module named 'nonexistent_pkg'",
            task_description="Run the test suite",
            approach_summary="`pip install nonexistent_pkg` — package doesn't exist",
            root_cause="Typo; real module is 'existing_pkg'",
            outcome=Outcome.RESOLVED,
            tags=["python", "import-error"],
        )
    )
    print(f"published: {published.trace_id}")

    # Later, when the next agent hits the same error:
    results = c.search(query="ModuleNotFoundError nonexistent_pkg")
    for hit in results.results:
        print(f"{hit.trace_id}: {hit.preview[:80]}")
```

Async mirror lives at `borg_collective.AsyncClient` — same surface,
awaitable methods, async context manager.

## Signed traces (optional, recommended for production)

Unsigned publishes work but are tagged `warning="unsigned_trace"` and
hold soft trust. To get a hard-trust signature on every publish, hand
the SDK an Ed25519 keypair:

```python
from borg_collective import Client, FailureTrace, Outcome, SigningKeyPair

kp = SigningKeyPair.generate()
with Client.from_config() as c:
    c.rotate_pubkey(kp.verify_key_hex)  # one-shot enrolment
    published = c.publish(my_trace, signing_key=kp)
    assert published.signed is True
```

The SDK computes `error_signature` locally, signs the canonical bytes
with your seed, and stamps `signature` + `signer_pubkey` into the body.
The Worker verifies before persisting; on a verifier mismatch you get
`SignatureError` with a `canonical-form drift` hint.

## Offline mode

Capture failures while disconnected; drain to the federation when
online:

```python
from borg_collective import Client

with Client.from_config(offline_mode=True) as c:
    c.publish(my_trace)            # enqueued to ~/.borg/offline_queue.sqlite
    c.publish(another_trace)       # also enqueued

with Client.from_config() as c:    # online again
    report = c.drain()
    print(f"drained {report.drained}, deferred {report.deferred}")
```

`offline_mode=True` is "always queue"; `offline_queue_path=...` on a
regular online Client is "queue as a fallback when the Worker is
unreachable". Either way the wire payload is identical — the queue
stores the same bytes the Worker eventually accepts.

## What's in the box

- `Client` / `AsyncClient` — sync + async with the same surface.
- `FailureTrace`, `Trace`, `TraceDetail`, `Amendment*`, `SearchRequest`,
  `FeedbackRequest` — typed wire models with strict validation
  (`extra="forbid"`) on inputs and forward-compatible reads
  (`extra="allow"`) on responses.
- `OfflineQueue` — SQLite-WAL persistent FIFO for disconnect-tolerant
  publishing.
- `borg_collective.testing.FakeClient` — drop-in replacement for tests
  that consume the SDK; record calls, queue responses, no HTTP.
- `borg` CLI — `init`, `register`, `whoami`, `publish`, `get`, `search`,
  `feedback`, `amend`, `drain`, `version`. `--json` on every command.
- Hermes adapter cookbook — wire Borg into agent frameworks. See
  `docs/cookbook/hermes-adapter.md`.

## Documentation

- **Examples** — `examples/01_publish_unsigned.py` …
  `examples/06_offline_drain.py`. Each runs against
  `FakeClient` with no args, or live with `BORG_API_KEY` set.
- **Cookbook** — `docs/cookbook/hermes-adapter.md` (real Hermes
  integration), `docs/cookbook/claude-code-mcp.md` (MCP server shape).
- **API reference + guides** — built with `mkdocs build`. Source under
  `docs/`.
- **Spec** — `docs/spec.md` mirrors the Worker's wire contract.

## Closure test

The **closure test** is the v1 done-gate: a signed failure trace
published by agent A round-trips through the federation, is verified
by agent B, and produces a measurable bidirectional signal — all
within a bounded time window. v0.1.3 ships the first measured run.

Headline numbers from the live federation, 2026-04-26 (20 trials,
N=20 default):

- **closure_rate: 100%** (20 / 20)
- median time-to-closure: **759 ms**
- p95: **851 ms** · p99: **871 ms**
- signature verification: **100%**
- feedback acceptance: **100%**

Run it yourself:

```bash
borg closure-test --trials 20
# or, on a system where `borg` collides with another binary:
python -m borg_collective.cli closure-test --trials 20
```

Exit 0 if closure_rate >= 95%, 1 if below, 2 on infrastructure
failure. The full schema, methodology, and failure-mode reference
live in [docs/closure-test.md](docs/closure-test.md). Daily
heartbeat against the live Worker runs in
`.github/workflows/closure.yml`.

## Federation status

The federation Worker at `borg-collective-v1.borg-farther.workers.dev`
is a development deployment — its corpus right now is mostly CI fixture
traces from the SDK's own integration test runs, not real-world
failures. Treat it as a working contract endpoint, not a curated
knowledge base. v1.0 will ship against a fresh corpus on a stable
production domain (currently provisioning a custom Cloudflare Workers
domain — final URL announced in the v1.0 release notes). The wire
contract, signing primitives, and SDK behaviour are stable across
versions; only the back-end data is in flux.

## License

MIT — see [LICENSE](LICENSE).

## Live federation Worker

`https://borg-collective-v1.borg-farther.workers.dev`

Status, source, and roadmap:
[borg-collective-v1](https://github.com/borg-farther/borg-collective-v1).
