backspin 0.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. backspin-0.5.1/.gitattributes +4 -0
  2. backspin-0.5.1/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
  3. backspin-0.5.1/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. backspin-0.5.1/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  5. backspin-0.5.1/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  6. backspin-0.5.1/.github/dependabot.yml +19 -0
  7. backspin-0.5.1/.github/workflows/ci.yml +82 -0
  8. backspin-0.5.1/.github/workflows/release.yml +60 -0
  9. backspin-0.5.1/.gitignore +19 -0
  10. backspin-0.5.1/.pre-commit-config.yaml +14 -0
  11. backspin-0.5.1/CHANGELOG.md +161 -0
  12. backspin-0.5.1/CODE_OF_CONDUCT.md +65 -0
  13. backspin-0.5.1/CONTRIBUTING.md +47 -0
  14. backspin-0.5.1/LICENSE +21 -0
  15. backspin-0.5.1/PKG-INFO +308 -0
  16. backspin-0.5.1/README.md +262 -0
  17. backspin-0.5.1/README.zh-CN.md +251 -0
  18. backspin-0.5.1/RELEASE.md +62 -0
  19. backspin-0.5.1/SECURITY.md +29 -0
  20. backspin-0.5.1/backspin/__init__.py +38 -0
  21. backspin-0.5.1/backspin/cli.py +379 -0
  22. backspin-0.5.1/backspin/cost.py +85 -0
  23. backspin-0.5.1/backspin/diff.py +121 -0
  24. backspin-0.5.1/backspin/export.py +71 -0
  25. backspin-0.5.1/backspin/fakes.py +151 -0
  26. backspin-0.5.1/backspin/integrations/__init__.py +0 -0
  27. backspin-0.5.1/backspin/integrations/anthropic.py +279 -0
  28. backspin-0.5.1/backspin/integrations/openai.py +298 -0
  29. backspin-0.5.1/backspin/proxy.py +367 -0
  30. backspin-0.5.1/backspin/py.typed +0 -0
  31. backspin-0.5.1/backspin/pytest_plugin.py +83 -0
  32. backspin-0.5.1/backspin/recorder.py +346 -0
  33. backspin-0.5.1/backspin/redaction.py +49 -0
  34. backspin-0.5.1/backspin/replay.py +327 -0
  35. backspin-0.5.1/backspin/runfile.py +182 -0
  36. backspin-0.5.1/backspin/server.py +91 -0
  37. backspin-0.5.1/backspin/share.py +58 -0
  38. backspin-0.5.1/backspin/testing.py +121 -0
  39. backspin-0.5.1/backspin/tui.py +130 -0
  40. backspin-0.5.1/backspin/ui/app.js +327 -0
  41. backspin-0.5.1/backspin/ui/i18n.js +75 -0
  42. backspin-0.5.1/backspin/ui/index.html +68 -0
  43. backspin-0.5.1/backspin/ui/style.css +214 -0
  44. backspin-0.5.1/docs/architecture.md +69 -0
  45. backspin-0.5.1/docs/format-spec.md +106 -0
  46. backspin-0.5.1/docs/ui-diff.png +0 -0
  47. backspin-0.5.1/docs/ui-timeline.png +0 -0
  48. backspin-0.5.1/examples/live_agent.py +36 -0
  49. backspin-0.5.1/examples/mock_agent.py +73 -0
  50. backspin-0.5.1/examples/what_if.py +80 -0
  51. backspin-0.5.1/pyproject.toml +124 -0
  52. backspin-0.5.1/sdks/typescript/LICENSE +21 -0
  53. backspin-0.5.1/sdks/typescript/README.md +34 -0
  54. backspin-0.5.1/sdks/typescript/package-lock.json +51 -0
  55. backspin-0.5.1/sdks/typescript/package.json +40 -0
  56. backspin-0.5.1/sdks/typescript/src/capture.ts +158 -0
  57. backspin-0.5.1/sdks/typescript/src/diff.ts +87 -0
  58. backspin-0.5.1/sdks/typescript/src/index.ts +13 -0
  59. backspin-0.5.1/sdks/typescript/src/recorder.ts +159 -0
  60. backspin-0.5.1/sdks/typescript/src/redaction.ts +41 -0
  61. backspin-0.5.1/sdks/typescript/src/replay.ts +80 -0
  62. backspin-0.5.1/sdks/typescript/src/runfile.ts +117 -0
  63. backspin-0.5.1/sdks/typescript/src/test/sdk.test.ts +258 -0
  64. backspin-0.5.1/sdks/typescript/tsconfig.build.json +5 -0
  65. backspin-0.5.1/sdks/typescript/tsconfig.json +14 -0
  66. backspin-0.5.1/tests/__init__.py +0 -0
  67. backspin-0.5.1/tests/conftest.py +25 -0
  68. backspin-0.5.1/tests/mock_anthropic_server.py +108 -0
  69. backspin-0.5.1/tests/mock_openai_server.py +157 -0
  70. backspin-0.5.1/tests/test_anthropic_integration.py +159 -0
  71. backspin-0.5.1/tests/test_branch.py +108 -0
  72. backspin-0.5.1/tests/test_branch_agent.py +86 -0
  73. backspin-0.5.1/tests/test_cli.py +106 -0
  74. backspin-0.5.1/tests/test_cost.py +55 -0
  75. backspin-0.5.1/tests/test_diff.py +54 -0
  76. backspin-0.5.1/tests/test_edge_cases.py +139 -0
  77. backspin-0.5.1/tests/test_export.py +68 -0
  78. backspin-0.5.1/tests/test_openai_capture.py +100 -0
  79. backspin-0.5.1/tests/test_openai_integration.py +212 -0
  80. backspin-0.5.1/tests/test_performance.py +47 -0
  81. backspin-0.5.1/tests/test_proxy.py +201 -0
  82. backspin-0.5.1/tests/test_pytest_plugin.py +48 -0
  83. backspin-0.5.1/tests/test_recorder.py +131 -0
  84. backspin-0.5.1/tests/test_redaction.py +110 -0
  85. backspin-0.5.1/tests/test_replay.py +103 -0
  86. backspin-0.5.1/tests/test_runfile.py +64 -0
  87. backspin-0.5.1/tests/test_server.py +82 -0
  88. backspin-0.5.1/tests/test_share.py +48 -0
  89. backspin-0.5.1/tests/test_spans.py +98 -0
  90. backspin-0.5.1/tests/test_tui.py +61 -0
@@ -0,0 +1,4 @@
1
+ # Normalize line endings across platforms
2
+ * text=auto eol=lf
3
+ *.png binary
4
+ *.whl binary
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something recorded / replayed / diffed wrong
4
+ labels: bug
5
+ ---
6
+
7
+ **What happened?** A clear description of the wrong behavior.
8
+
9
+ **Minimal reproduction**
10
+
11
+ ```python
12
+ # the smallest agent snippet that shows it
13
+ ```
14
+
15
+ **The run file.** backspin is built so a bug report can be a `*.backspin.jsonl`
16
+ file — attach it when you can (redact first if it contains sensitive content,
17
+ see the README's redaction section). If not, include:
18
+
19
+ - backspin version (`pip show backspin`)
20
+ - Python version, OS
21
+ - how the run was captured: `capture_openai` / `capture_anthropic` / proxy / TS SDK
22
+ - provider and protocol (OpenAI-compatible / Anthropic Messages)
23
+
24
+ **What did you expect?**
25
+
26
+ **Anything else?** Tracebacks, warnings (`ReplayMismatchWarning`?), screenshots of the viewer.
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Questions & usage help
4
+ url: https://github.com/zaibuchihuoji/backspin/discussions
5
+ about: Ask how to record/replay/diff your agent — we'll answer.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest a capability for the recorder / replay / diff workflow
4
+ labels: enhancement
5
+ ---
6
+
7
+ **Your use case.** What are you trying to debug that backspin doesn't help with today?
8
+
9
+ **Proposed solution.** What should it do? An API sketch is welcome:
10
+
11
+ ```python
12
+ # e.g. rec.capture_x(...) / backspin some-command ...
13
+ ```
14
+
15
+ **Alternatives you considered.** Cloud tools, manual logging, etc.
16
+
17
+ **Does it fit the project's scope?** backspin stays local-first, file-based,
18
+ and dependency-free at its core — features that need a server or an account
19
+ belong elsewhere.
@@ -0,0 +1,15 @@
1
+ ## What & why
2
+
3
+ <!-- One or two sentences: what changes, and why a user would care. -->
4
+
5
+ ## Checklist
6
+
7
+ - [ ] `pytest` green locally (including integration + performance suites)
8
+ - [ ] `ruff check backspin/ tests/` clean
9
+ - [ ] `mypy backspin/` clean
10
+ - [ ] Behavior change has a test (run-format changes need a round-trip test)
11
+ - [ ] `CHANGELOG.md` updated under an "Unreleased" heading
12
+ - [ ] No new core dependencies (`backspin/` stays stdlib-only outside extras)
13
+
14
+ <!-- If this touches the run file format: SCHEMA_VERSION bumped + load_run
15
+ still reads old files. -->
@@ -0,0 +1,19 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ directory: "/"
5
+ schedule:
6
+ interval: weekly
7
+ groups:
8
+ python-dev-deps:
9
+ applies-to: version-updates
10
+ patterns:
11
+ - "*"
12
+ - package-ecosystem: npm
13
+ directory: "/sdks/typescript"
14
+ schedule:
15
+ interval: weekly
16
+ - package-ecosystem: github-actions
17
+ directory: "/"
18
+ schedule:
19
+ interval: monthly
@@ -0,0 +1,82 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ lint:
14
+ runs-on: ubuntu-latest
15
+ timeout-minutes: 10
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+ cache: pip
22
+ - run: pip install ruff
23
+ - run: ruff check backspin/ tests/
24
+
25
+ typecheck:
26
+ runs-on: ubuntu-latest
27
+ timeout-minutes: 10
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - uses: actions/setup-python@v5
31
+ with:
32
+ python-version: "3.12"
33
+ cache: pip
34
+ - run: pip install -e ".[dev]"
35
+ - run: mypy backspin/
36
+
37
+ test:
38
+ strategy:
39
+ fail-fast: false
40
+ matrix:
41
+ os: [ubuntu-latest, windows-latest, macos-latest]
42
+ python: ["3.9", "3.10", "3.11", "3.12", "3.13"]
43
+ exclude:
44
+ - os: windows-latest
45
+ python: "3.9"
46
+ - os: windows-latest
47
+ python: "3.10"
48
+ - os: macos-latest
49
+ python: "3.9"
50
+ runs-on: ${{ matrix.os }}
51
+ timeout-minutes: 20
52
+ steps:
53
+ - uses: actions/checkout@v4
54
+ - uses: actions/setup-python@v5
55
+ with:
56
+ python-version: ${{ matrix.python }}
57
+ cache: pip
58
+ - run: pip install -e ".[dev]"
59
+ # 3.9 skips the anthropic SDK (dropped 3.9), which drags total
60
+ # coverage below the normal gate; relax just that leg.
61
+ - run: >
62
+ pytest -q --cov=backspin --cov-report=xml --cov-report=term
63
+ ${{ matrix.python == '3.9' && '--cov-fail-under=60' || '' }}
64
+
65
+ typescript:
66
+ runs-on: ubuntu-latest
67
+ timeout-minutes: 10
68
+ steps:
69
+ - uses: actions/checkout@v4
70
+ - uses: actions/setup-node@v4
71
+ with:
72
+ node-version: "20"
73
+ - working-directory: sdks/typescript
74
+ run: |
75
+ npm ci
76
+ npm test
77
+ # the published tarball must not ship tests and must typecheck clean
78
+ - working-directory: sdks/typescript
79
+ run: |
80
+ rm -rf dist
81
+ npm run build
82
+ test ! -d dist/test
@@ -0,0 +1,60 @@
1
+ name: Release
2
+
3
+ # Push a tag `vX.Y.Z` to publish. PyPI uses trusted publishing (no token):
4
+ # configure the "backspin" project on pypi.org once —
5
+ # Settings > Publishing > add GitHub publisher (repo: zaibuchihuoji/backspin,
6
+ # workflow: release.yml, environment: pypi).
7
+ on:
8
+ push:
9
+ tags: ["v*"]
10
+
11
+ permissions:
12
+ id-token: write # PyPI trusted publishing (OIDC)
13
+
14
+ jobs:
15
+ build-python:
16
+ runs-on: ubuntu-latest
17
+ timeout-minutes: 15
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.12"
23
+ cache: pip
24
+ - run: pip install -e ".[dev]" build
25
+ - run: ruff check backspin/ tests/
26
+ - run: mypy backspin/
27
+ - run: pytest -q
28
+ - run: python -m build
29
+ - uses: actions/upload-artifact@v4
30
+ with:
31
+ name: dist
32
+ path: dist/
33
+
34
+ publish-pypi:
35
+ needs: build-python
36
+ runs-on: ubuntu-latest
37
+ environment: pypi
38
+ steps:
39
+ - uses: actions/download-artifact@v4
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+ - name: Publish to PyPI
44
+ uses: pypa/gh-action-pypi-publish@release/v1
45
+
46
+ build-typescript:
47
+ runs-on: ubuntu-latest
48
+ timeout-minutes: 10
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+ - uses: actions/setup-node@v4
52
+ with:
53
+ node-version: "20"
54
+ - working-directory: sdks/typescript
55
+ run: |
56
+ npm ci
57
+ npm test
58
+ npm run build
59
+ # npm publish stays manual until an NPM_TOKEN secret is configured:
60
+ # npm publish --access public (from sdks/typescript, on the tag)
@@ -0,0 +1,19 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ runs/
13
+ *.backspin.jsonl
14
+ node_modules/
15
+ sdks/typescript/dist/
16
+ .coverage
17
+ coverage.xml
18
+ .mimosa/
19
+ .DS_Store
@@ -0,0 +1,14 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.16.5
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - repo: https://github.com/pre-commit/pre-commit-hooks
8
+ rev: v5.0.0
9
+ hooks:
10
+ - id: end-of-file-fixer
11
+ - id: trailing-whitespace
12
+ - id: check-json
13
+ - id: check-toml
14
+ - id: check-merge-conflict
@@ -0,0 +1,161 @@
1
+ # Changelog
2
+
3
+ All notable changes to backspin are documented here.
4
+
5
+ ## 0.5.1 — 2026-08-29
6
+
7
+ Hardening release: engineering-quality fixes found in a full project audit.
8
+
9
+ - **TS SDK: async tools recorded correctly** — `Recorder.tool()` now awaits
10
+ async functions, recording the resolved value (previously a Promise, which
11
+ serialized to `{}`) with the real duration; async rejections are recorded
12
+ with their error. A stream the consumer abandons mid-loop is now still
13
+ recorded (`finalize` runs in `finally`). `diffRuns` no longer falls back to
14
+ `"[object Object]"` for fingerprint-less events.
15
+ - **TS SDK: redaction parity** — `redactStrings(transform)` / `mask(regex)`
16
+ mirror the Python API; `Recorder({ redact })` keeps secrets out of run
17
+ files while structural fields stay readable. npm packaging hardened
18
+ (`files`, `exports`, `engines`, `prepublishOnly`, SDK LICENSE/README,
19
+ tests excluded from the tarball). Version sync: Python and TS SDK both
20
+ 0.5.1.
21
+ - **Recorder survives disk failures** — a run file that becomes unwritable
22
+ (disk full, closed handle) stops recording with one `RuntimeWarning`
23
+ instead of raising into instrumented agent code (a stated ground rule).
24
+ - **Stream context managers no longer swallow exceptions** — `__exit__` in
25
+ both OpenAI and Anthropic stream wrappers returned from a `finally`
26
+ block, which could discard in-flight exceptions; rewritten.
27
+ - **Proxy hardening** — non-object JSON bodies return a clean 400 instead
28
+ of an unhandled 500 (both `/v1/chat/completions` and `/v1/messages`).
29
+ - **Viewer API** — a well-formed but missing run name returns 404 instead
30
+ of a 500; corrupt runs still return 422.
31
+ - **Viewer UI** — language toggle re-renders in place (no page reload),
32
+ `title` attributes and `<html lang>` localize, cards escape by default,
33
+ run list / timeline rows / tabs are keyboard-operable (roles, tabindex,
34
+ Enter/Space, Esc closes the inspector, focus rings).
35
+ - **Tooling** — ruff (curated rule set) and mypy are clean across the
36
+ codebase and enforced in CI; fixed real findings (`share.py` missing
37
+ `Any` import, `replay.py` missing `Callable` import). pytest-cov wired
38
+ in. CI: lint + typecheck + test matrix (adds macOS and Python 3.13) +
39
+ a TypeScript job (tests + publish-shape check); release workflow builds
40
+ and publishes to PyPI via trusted publishing on `v*` tags.
41
+ - **Community/meta** — SECURITY.md (incl. the recordings-contain-secrets
42
+ threat model), CODE_OF_CONDUCT.md, issue templates, PR checklist,
43
+ Dependabot, pre-commit config, README badges (CI/PyPI/license/ruff),
44
+ SPDX license expression, PyPI URLs; RELEASE.md rewritten as a generic
45
+ maintainer handbook.
46
+ - Tests: Python 116 (was 107), TypeScript 10 (was 6).
47
+
48
+ ## 0.5.0 — 2026-08-29
49
+
50
+ - **Agent-level what-if** — `branch_agent(fn, run, mutations)`: re-run the
51
+ actual agent function against a mutated cassette. Unlike `branch()`
52
+ (request-sequence replay), the full run shape — logs, tool calls, spans,
53
+ downstream requests — is regenerated for real, and `diff_runs` pins the
54
+ first divergence at the exact event where the mutation took effect.
55
+ `examples/what_if.py` demonstrates both levels.
56
+
57
+ ## 0.4.1 — 2026-08-29
58
+
59
+ - **Viewer i18n** — the local viewer is now Chinese-first with an EN/中文
60
+ toggle in the header (choice persisted in localStorage); share files
61
+ inherit the same interface. README screenshots updated.
62
+ - `share` also inlines the new `i18n.js` asset.
63
+
64
+ ## 0.4.0 — 2026-08-29
65
+
66
+ Multi-provider release: Anthropic natively, TypeScript SDK, export/share/TUI.
67
+
68
+ - **Anthropic native** — `rec.capture_anthropic(client)`: sync/async/
69
+ streaming capture of the Messages API (text + tool_use blocks), events
70
+ recorded with `provider="anthropic"` and usage normalized to
71
+ `prompt_tokens`/`completion_tokens` so costs/diffs work cross-provider.
72
+ `backspin proxy` gains a `/v1/messages` adapter (record + replay,
73
+ streaming included) — verified end-to-end against the real anthropic SDK.
74
+ - **TypeScript SDK** (`sdks/typescript`, `@backspin/sdk`) — Recorder with
75
+ AsyncLocalStorage spans, `captureOpenAI` (sync/async/streaming),
76
+ `Cassette`/`stubClient` replay, `diffRuns`. Same `.backspin.jsonl` run
77
+ format — Python recordings replay in TypeScript and vice versa. 6 node:test
78
+ suites.
79
+ - **Dataset export** — `backspin export <run> --format pairs|sft` turns
80
+ recordings into eval/fine-tune JSONL.
81
+ - **Single-file sharing** — `backspin share <run>` bundles the run + the
82
+ whole viewer into one HTML file; opens in any browser, zero install.
83
+ - **TUI** — `backspin tui`: keyboard-driven terminal viewer (runs → timeline
84
+ → step JSON), injectable IO, fully unit-tested.
85
+ - Python suite: 104 tests; TypeScript suite: 6 tests.
86
+
87
+ ## 0.3.0 — 2026-08-29
88
+
89
+ The "real debugger" release: what-if branching, zero-code proxy
90
+ integration, structured spans, and costs.
91
+
92
+ - **What-if branching** — `Cassette.mutate()` alters a recorded answer;
93
+ `backspin.replay.branch()` records the mutated replay as a new run
94
+ (`branch_of` metadata), rewriting downstream requests so mutations
95
+ propagate; `diff_runs(..., llm_only=True)` and
96
+ `backspin branch <file> --step N --content "..."` pinpoint where the
97
+ timelines split.
98
+ - **`backspin proxy`** — OpenAI-compatible local proxy in two modes:
99
+ *record* (forward to upstream, capture everything, streaming included —
100
+ zero-code integration for any framework/language) and *replay* (serve a
101
+ recorded run back as an API, no network). Authorization headers are
102
+ forwarded but never recorded.
103
+ - **Spans** — `with rec.span(name, meta=...)`: nested agent → tool →
104
+ sub-LLM structure. Events carry `span_id`/`depth`; async-safe via
105
+ contextvars (per-task stacks); exceptions recorded on the exit event;
106
+ span durations excluded from run totals to avoid double counting.
107
+ - **Costs** — built-in per-1M-token price table (OpenAI, Claude, Gemini,
108
+ DeepSeek; exact-then-longest-prefix matching), `cost_usd` /
109
+ `cost_complete` in run totals, cost card in the viewer, `~$` in `show`.
110
+ - **pytest plugin** — `backspin` fixture (pytest11 entry point) with
111
+ strict `assert_replays_identically()`: order-fallback replays are
112
+ failures, only fingerprint-exact replay passes.
113
+ - **Viewer** — span tree indentation, cost card.
114
+ - **Docs** — `docs/format-spec.md` (the run file specification with
115
+ versioning rules) and `docs/architecture.md`.
116
+ - Internal: diff signatures understand span events; `_usage_of` fixes
117
+ streamed usage extraction; stream wrappers gain context-manager/close
118
+ passthrough.
119
+
120
+ ## 0.2.0 — 2026-08-29
121
+
122
+ Hardening release: the record → replay → diff → view loop is now verified
123
+ against the real OpenAI SDK and guarded by edge-case and performance tests.
124
+
125
+ - **Real-SDK integration suite** — a local OpenAI-compatible HTTP/SSE mock
126
+ server runs the genuine `openai` SDK through sync, async, streaming
127
+ (tool-call deltas, `include_usage`), context-manager and error paths.
128
+ Found and fixed two real bugs:
129
+ - async resource detection (modern SDKs ship async methods that are not
130
+ coroutine functions — detection now uses the resource type name)
131
+ - streamed `usage` chunks were dropped in stream reconstruction
132
+ - **Redaction API** — `Recorder(redact=...)` plus
133
+ `backspin.redaction.redact_strings` / `mask`. Structural fields (model,
134
+ tool name, fingerprint, durations) stay in clear; every other payload
135
+ value — including unknown custom keys — goes through the redactor.
136
+ Fingerprints are computed pre-redaction so replay matching is unaffected.
137
+ - **Streaming wrappers** now pass through `close()`, the async/with context
138
+ manager protocols and positional args.
139
+ - **Performance guards** — 20k-event record/load and 2×4000-event diff stay
140
+ in the seconds range.
141
+ - **Edge-case suite** — unicode round-trips, concurrent recorders,
142
+ thread-safe single recorder, writes after close, circular references,
143
+ empty cassettes, corrupt run files over the viewer API, CLI `--json`.
144
+ - **Packaging** — wheel verified to ship the viewer UI assets and
145
+ `py.typed`.
146
+
147
+ ## 0.1.0 — 2026-08-29
148
+
149
+ Initial MVP.
150
+
151
+ - `Recorder`: single-file JSONL run format (`.backspin.jsonl`), header +
152
+ ordered steps, error capture with tracebacks, custom events
153
+ - OpenAI-shaped capture: sync / async / streaming, tool-call reconstruction
154
+ - `@rec.tool` decorator (sync + async)
155
+ - Deterministic replay: `Cassette`, `stub_client`, `patch_openai`
156
+ (fingerprint match, order fallback with warning)
157
+ - `diff_runs`: step alignment by signature, first-divergence detection
158
+ - CLI: `backspin ls / show / diff / ui`
159
+ - Local viewer: FastAPI + zero-build vanilla JS (waterfall timeline, step
160
+ inspector, side-by-side diff)
161
+ - `backspin.testing.FakeOpenAI` for keyless demos and tests
@@ -0,0 +1,65 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment include:
18
+
19
+ * Demonstrating empathy and kindness toward other people
20
+ * Being respectful of differing opinions, viewpoints, and experiences
21
+ * Giving and gracefully accepting constructive feedback
22
+ * Accepting responsibility and apologizing to those affected by our mistakes,
23
+ and learning from the experience
24
+ * Focusing on what is best not just for us as individuals, but for the overall
25
+ community
26
+
27
+ Examples of unacceptable behavior include:
28
+
29
+ * The use of sexualized language or imagery, and sexual attention or advances
30
+ of any kind
31
+ * Trolling, insulting or derogatory comments, and personal or political attacks
32
+ * Public or private harassment
33
+ * Publishing others' private information, such as a physical or email address,
34
+ without their explicit permission
35
+ * Other conduct which could reasonably be considered inappropriate in a
36
+ professional setting
37
+
38
+ ## Enforcement Responsibilities
39
+
40
+ Community leaders are responsible for clarifying and enforcing our standards
41
+ of acceptable behavior and will take appropriate and fair corrective action in
42
+ response to any behavior that they deem inappropriate, threatening, offensive,
43
+ or harmful.
44
+
45
+ ## Scope
46
+
47
+ This Code of Conduct applies within all community spaces, and also applies
48
+ when an individual is officially representing the community in public spaces.
49
+
50
+ ## Enforcement
51
+
52
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
53
+ reported to the community leaders responsible for enforcement via a private
54
+ message to the maintainers or via [GitHub's private vulnerability/advisory
55
+ channel](https://github.com/zaibuchihuoji/backspin/security/advisories/new) if
56
+ private. All complaints will be reviewed and investigated promptly and fairly.
57
+
58
+ All community leaders are obligated to respect the privacy and security of the
59
+ reporter of any incident.
60
+
61
+ ## Attribution
62
+
63
+ This Code of Conduct is adapted from the [Contributor Covenant](
64
+ https://www.contributor-covenant.org), version 2.1, available at
65
+ https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
@@ -0,0 +1,47 @@
1
+ # Contributing to backspin
2
+
3
+ Thanks for helping make agent debugging less painful.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ git clone <your fork>
9
+ cd backspin
10
+ python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
11
+ pip install -e ".[dev]"
12
+ pytest
13
+ ruff check backspin/ tests/ # must be clean
14
+ mypy backspin/ # must be clean
15
+ ```
16
+
17
+ `pytest` runs the full suite, including the real-SDK integration tests
18
+ (they talk to a local OpenAI-compatible mock server — no network, no API
19
+ key needed). `pip install pre-commit && pre-commit install` wires the same
20
+ lint into your commits.
21
+
22
+ ## Ground rules
23
+
24
+ - **Core stays dependency-free.** `backspin/` (except the `ui` extra)
25
+ must import nothing beyond the standard library. If you need a library,
26
+ it belongs in an extra or in the test suite.
27
+ - **A recorder must never crash the agent.** Serialization problems degrade
28
+ to reprs; exceptions in recorded code are captured and re-raised. Keep it
29
+ that way.
30
+ - **Every behavior change needs a test.** If it touches the run format,
31
+ add a round-trip test (record → `load_run` → assert).
32
+ - **The run file format is a contract.** Bump `SCHEMA_VERSION` in
33
+ `runfile.py` and document the change in `CHANGELOG.md` if you must break
34
+ it — then keep `load_run` able to read old files.
35
+
36
+ ## Style
37
+
38
+ - Type-hinted Python, `from __future__ import annotations`, 3.9+ compatible.
39
+ - ASCII-only CLI output (Windows consoles); unicode is fine in file content.
40
+ - Comments explain constraints, not mechanics.
41
+
42
+ ## Submitting
43
+
44
+ 1. Branch from `main`.
45
+ 2. `pytest` green, `ruff check` and `mypy` clean — CI enforces all three.
46
+ 3. Update `CHANGELOG.md` under an "Unreleased" heading.
47
+ 4. Open a PR describing the user-visible change (the template has the checklist).
backspin-0.5.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 backspin contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.