redundo 0.1.0__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 (68) hide show
  1. redundo-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +51 -0
  2. redundo-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
  3. redundo-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +42 -0
  4. redundo-0.1.0/.github/pull_request_template.md +10 -0
  5. redundo-0.1.0/.github/workflows/ci.yml +29 -0
  6. redundo-0.1.0/.github/workflows/publish.yml +45 -0
  7. redundo-0.1.0/.gitignore +8 -0
  8. redundo-0.1.0/CODE_OF_CONDUCT.md +64 -0
  9. redundo-0.1.0/CONTRIBUTING.md +129 -0
  10. redundo-0.1.0/LICENSE +21 -0
  11. redundo-0.1.0/PKG-INFO +390 -0
  12. redundo-0.1.0/README.md +364 -0
  13. redundo-0.1.0/docs/claude-code.md +425 -0
  14. redundo-0.1.0/docs/cowork.md +179 -0
  15. redundo-0.1.0/docs/hashing.md +127 -0
  16. redundo-0.1.0/docs/openclaw.md +204 -0
  17. redundo-0.1.0/docs/openinference.md +73 -0
  18. redundo-0.1.0/examples/demo_report.html +83 -0
  19. redundo-0.1.0/examples/demo_trace.jsonl +31 -0
  20. redundo-0.1.0/pyproject.toml +47 -0
  21. redundo-0.1.0/src/redundo/__init__.py +10 -0
  22. redundo-0.1.0/src/redundo/adapter/__init__.py +13 -0
  23. redundo-0.1.0/src/redundo/adapter/cli.py +135 -0
  24. redundo-0.1.0/src/redundo/adapter/collector.py +171 -0
  25. redundo-0.1.0/src/redundo/adapter/detect.py +136 -0
  26. redundo-0.1.0/src/redundo/adapter/hashing.py +145 -0
  27. redundo-0.1.0/src/redundo/adapter/otlp.py +207 -0
  28. redundo-0.1.0/src/redundo/adapter/sources/__init__.py +6 -0
  29. redundo-0.1.0/src/redundo/adapter/sources/claude_code.py +830 -0
  30. redundo-0.1.0/src/redundo/adapter/sources/cowork.py +378 -0
  31. redundo-0.1.0/src/redundo/adapter/sources/openclaw.py +466 -0
  32. redundo-0.1.0/src/redundo/adapter/sources/openinference.py +482 -0
  33. redundo-0.1.0/src/redundo/adapter/writer.py +20 -0
  34. redundo-0.1.0/src/redundo/analyzer/__init__.py +37 -0
  35. redundo-0.1.0/src/redundo/analyzer/classify.py +241 -0
  36. redundo-0.1.0/src/redundo/analyzer/cli.py +104 -0
  37. redundo-0.1.0/src/redundo/analyzer/cycles.py +96 -0
  38. redundo-0.1.0/src/redundo/analyzer/ingest.py +121 -0
  39. redundo-0.1.0/src/redundo/analyzer/lineage.py +118 -0
  40. redundo-0.1.0/src/redundo/analyzer/metrics.py +244 -0
  41. redundo-0.1.0/src/redundo/analyzer/report.py +434 -0
  42. redundo-0.1.0/src/redundo/analyzer/schema.py +171 -0
  43. redundo-0.1.0/src/redundo/cli.py +58 -0
  44. redundo-0.1.0/tests/adapter/fixtures/sample_otlp.json +94 -0
  45. redundo-0.1.0/tests/adapter/helpers.py +113 -0
  46. redundo-0.1.0/tests/adapter/test_claude_code.py +383 -0
  47. redundo-0.1.0/tests/adapter/test_cli.py +124 -0
  48. redundo-0.1.0/tests/adapter/test_cowork.py +183 -0
  49. redundo-0.1.0/tests/adapter/test_detect.py +106 -0
  50. redundo-0.1.0/tests/adapter/test_hashing.py +192 -0
  51. redundo-0.1.0/tests/adapter/test_openclaw.py +305 -0
  52. redundo-0.1.0/tests/adapter/test_openinference.py +255 -0
  53. redundo-0.1.0/tests/adapter/test_otlp.py +70 -0
  54. redundo-0.1.0/tests/analyzer/fixtures/malformed.jsonl +4 -0
  55. redundo-0.1.0/tests/analyzer/fixtures/sample.jsonl +18 -0
  56. redundo-0.1.0/tests/analyzer/test_analyzer_cli.py +32 -0
  57. redundo-0.1.0/tests/analyzer/test_classify.py +254 -0
  58. redundo-0.1.0/tests/analyzer/test_coverage.py +152 -0
  59. redundo-0.1.0/tests/analyzer/test_cycles.py +92 -0
  60. redundo-0.1.0/tests/analyzer/test_end_to_end.py +71 -0
  61. redundo-0.1.0/tests/analyzer/test_hash_spec_guard.py +34 -0
  62. redundo-0.1.0/tests/analyzer/test_ingest.py +51 -0
  63. redundo-0.1.0/tests/analyzer/test_lineage.py +78 -0
  64. redundo-0.1.0/tests/analyzer/test_metrics.py +107 -0
  65. redundo-0.1.0/tests/analyzer/test_report_html.py +136 -0
  66. redundo-0.1.0/tests/analyzer/test_schema.py +74 -0
  67. redundo-0.1.0/tests/test_redundo_cli.py +94 -0
  68. redundo-0.1.0/uv.lock +194 -0
@@ -0,0 +1,51 @@
1
+ name: Bug report
2
+ description: Something in redundo produced a wrong, missing, or misleading result.
3
+ labels: ["bug"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for reporting this. redundo's whole design point is "never guess past a genuine unknown" -- so a case where it guessed wrong, or where its degrade-honestly behavior is confusing, is exactly the kind of bug report that matters most.
9
+
10
+ - type: dropdown
11
+ id: component
12
+ attributes:
13
+ label: Where does this happen?
14
+ options:
15
+ - redundo adapt (a specific source: openinference, claude-code, cowork, openclaw)
16
+ - redundo analyze (classification, coverage, report output)
17
+ - redundo collect
18
+ - CLI / packaging
19
+ - Documentation
20
+ - Not sure
21
+ validations:
22
+ required: true
23
+
24
+ - type: textarea
25
+ id: what-happened
26
+ attributes:
27
+ label: What happened?
28
+ description: What you expected vs. what you actually got.
29
+ validations:
30
+ required: true
31
+
32
+ - type: textarea
33
+ id: repro
34
+ attributes:
35
+ label: Minimal reproduction
36
+ description: >
37
+ A small trace (JSONL for `analyze`, or a captured OTLP batch for `adapt`) that
38
+ reproduces this, plus the exact command you ran. Redact any real prompt/tool
39
+ content first -- content_hash exists so you never have to share raw content to
40
+ report a bug.
41
+ render: shell
42
+ validations:
43
+ required: true
44
+
45
+ - type: input
46
+ id: version
47
+ attributes:
48
+ label: redundo version
49
+ description: Output of `pip show redundo` (the Version line), or the commit/tag you installed from.
50
+ validations:
51
+ required: true
@@ -0,0 +1 @@
1
+ blank_issues_enabled: true
@@ -0,0 +1,42 @@
1
+ name: Feature request
2
+ description: A new source, a new analysis, or a change to adapt/analyze's behavior.
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Before proposing a new adapter source or analysis, skim [CONTRIBUTING.md](../../CONTRIBUTING.md) --
9
+ both are held to specific discipline (real captured telemetry, honest coverage
10
+ reporting, never guessing past a genuine unknown) that shapes what a good PR
11
+ for either looks like.
12
+
13
+ - type: dropdown
14
+ id: kind
15
+ attributes:
16
+ label: What kind of change is this?
17
+ options:
18
+ - New adapter source (a new agent framework's OTLP)
19
+ - New analysis (something other than redundant-call detection)
20
+ - Change to an existing source or analysis
21
+ - CLI / packaging
22
+ - Other
23
+ validations:
24
+ required: true
25
+
26
+ - type: textarea
27
+ id: problem
28
+ attributes:
29
+ label: What's the problem or gap?
30
+ description: What can't you do with redundo today that you want to?
31
+ validations:
32
+ required: true
33
+
34
+ - type: textarea
35
+ id: proposal
36
+ attributes:
37
+ label: Proposed approach
38
+ description: >
39
+ If this is a new source: what does its real OTLP/telemetry shape look like,
40
+ and do you have a real capture to validate against? (See CONTRIBUTING.md --
41
+ every source in this project was built against actual captured data, not
42
+ just a framework's docs.)
@@ -0,0 +1,10 @@
1
+ ## What changed and why
2
+
3
+ <!-- The "why" matters more than the "what" here -- see CONTRIBUTING.md. -->
4
+
5
+ ## Checklist
6
+
7
+ - [ ] `uv run pytest` passes locally
8
+ - [ ] New behavior has test coverage (a fixture, not just a real capture -- real captures are for validation, too brittle/large to be the actual test)
9
+ - [ ] If this touches an adapter source or the analyzer: coverage/known-gaps are still reported honestly, nothing silently guesses past an unknown
10
+ - [ ] Docs (`README.md`, `docs/*.md`) updated if this changes documented behavior
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ name: test (py${{ matrix.python-version }})
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.10", "3.11", "3.12"]
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v3
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Install dependencies
26
+ run: uv sync --extra collector
27
+
28
+ - name: Run tests
29
+ run: uv run pytest -q
@@ -0,0 +1,45 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build:
9
+ name: Build distribution
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v3
16
+ with:
17
+ python-version: "3.12"
18
+
19
+ - name: Build sdist and wheel
20
+ run: uv build
21
+
22
+ - name: Upload build artifacts
23
+ uses: actions/upload-artifact@v4
24
+ with:
25
+ name: dist
26
+ path: dist/
27
+
28
+ publish:
29
+ name: Publish
30
+ needs: build
31
+ runs-on: ubuntu-latest
32
+ environment:
33
+ name: pypi
34
+ url: https://pypi.org/project/redundo/
35
+ permissions:
36
+ id-token: write # required for PyPI Trusted Publishing (OIDC) -- no API token stored anywhere
37
+ steps:
38
+ - name: Download build artifacts
39
+ uses: actions/download-artifact@v4
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+
44
+ - name: Publish to PyPI
45
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .DS_Store
@@ -0,0 +1,64 @@
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:
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:
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
+ Project maintainers 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 maintainers by opening a private security advisory or issue on
54
+ this repository. All complaints will be reviewed and investigated promptly and
55
+ fairly.
56
+
57
+ ## Attribution
58
+
59
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
60
+ version 2.1, available at
61
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
62
+
63
+ [homepage]: https://www.contributor-covenant.org
64
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
@@ -0,0 +1,129 @@
1
+ # Contributing
2
+
3
+ By participating in this project you agree to abide by its
4
+ [Code of Conduct](CODE_OF_CONDUCT.md).
5
+
6
+ This is two packages under one name: `redundo.adapter` turns a
7
+ specific trace source into the schema, `redundo.analyzer` runs
8
+ analyses over that schema. They share the schema contract and nothing
9
+ else at runtime — contribute to either independently.
10
+
11
+ ## Workflow
12
+
13
+ - **Every change goes through a branch and a pull request against
14
+ `main`** — including maintainers' own changes. `main` is protected:
15
+ direct pushes are rejected, and CI must pass before a PR can merge.
16
+ - **Branch names**: `fix/…` for bug fixes, `feat/…` for new sources or
17
+ analyses, `chore/…` for anything else (docs, CI, tooling).
18
+ - **Bug reports and feature requests** go through the issue templates
19
+ (`.github/ISSUE_TEMPLATE/`) rather than an ad-hoc issue — the bug
20
+ report template in particular asks for a minimal reproduction (a
21
+ small fixture, not a real capture) and the redundo version, which is
22
+ most of what's needed to act on it.
23
+ - **CI** (`.github/workflows/ci.yml`) runs the full test suite on
24
+ Python 3.10–3.12 for every PR. A PR that doesn't pass CI doesn't get
25
+ merged, no exception for "it's just docs" — the workflow itself is
26
+ the only thing verifying that.
27
+
28
+ ## Adding a new source (`redundo.adapter`)
29
+
30
+ Each source lives in its own module under `src/redundo/adapter/sources/`.
31
+ A good addition:
32
+
33
+ 1. **Captures real telemetry first.** Every existing source's `docs/*.md`
34
+ was written against actual captured OTLP JSON, not just the source's
35
+ published documentation — several documented behaviors turned out to
36
+ be wrong or incomplete once checked against real data. Do the same:
37
+ enable the source's telemetry, point it at a local collector (see
38
+ `docs/` for examples), and look at what actually comes out before
39
+ writing conversion logic against it.
40
+ 2. **Writes a `docs/<source>.md`** covering: how task/session grouping
41
+ works, what content is and isn't observable (and under which flags),
42
+ how tool calls and their results are represented, and a "known gaps"
43
+ section stating plainly what can't be recovered and why. Copy the
44
+ structure of an existing source doc.
45
+ 3. **Adds a detection rule** in `detect.py` — something in the data
46
+ itself (a span name, an attribute, a resource attribute) that
47
+ reliably distinguishes this source from every other one already
48
+ supported. If nothing does, say so in the docstring and require
49
+ `--source` instead of guessing.
50
+ 4. **Never fabricates content.** If a result or a piece of content isn't
51
+ observable, omit the record or mark it explicitly rather than
52
+ inventing a placeholder hash — see `hashing.py`'s module docstring and
53
+ any existing source's handling of unobservable content for why: a
54
+ placeholder hash that happens to match (or never match) another
55
+ record's hash reads as a real finding to everything downstream.
56
+ 5. **Tests against hand-built fixtures**, not just real captures (which
57
+ are convenient for validation but too large/brittle to check in) --
58
+ see `tests/adapter/helpers.py` for the fixture builders every existing
59
+ source's tests use.
60
+
61
+ ## Adding a new analysis (`redundo.analyzer`)
62
+
63
+ The schema and ingest layer (`schema.py`, `ingest.py`) and the reporting
64
+ infrastructure (`report.py`'s coverage plumbing, `to_text()`/`to_json()`/
65
+ `to_html()`) are not specific to waste detection. `classify.py`,
66
+ `cycles.py`, and the candidate-pair parts of `lineage.py` are — that's
67
+ today's one analysis, not the whole package.
68
+
69
+ A new analysis over the same schema should:
70
+
71
+ 1. **Reuse `load_events()` and the `Event` dataclass** — never invent a
72
+ parallel ingestion path. The schema is the contract every adapter
73
+ targets; a second analysis reading something else defeats the point
74
+ of having one.
75
+ 2. **Report coverage honestly**, the same way the waste analysis does:
76
+ what fraction of the loaded corpus this analysis can actually speak
77
+ to, before any headline number. See `metrics.py`'s `CoverageStats`
78
+ and `report.py`'s coverage lines for the pattern to follow.
79
+ 3. **Never guess past a genuine unknown.** `classify.py`'s three-signal
80
+ model (waste-supporting / legit-supporting / unknown, never a forced
81
+ binary) and its module docstring are the reference for why: a
82
+ confident wrong answer is worse than an honest "the trace doesn't
83
+ say," because the first time someone spot-checks a confident answer
84
+ by hand and finds it wrong, the tool stops being trusted.
85
+ 4. **Prints its rule next to its own counts**, not just a label — see
86
+ `RULE_TEXT` in `report.py`. A number without the rule that produced it
87
+ isn't checkable by anyone reading the report.
88
+ 5. **Ships with a fixture-based validation**, the same discipline
89
+ `tests/analyzer/fixtures/sample.jsonl` follows: hand-built traces with
90
+ a known, intended classification for each case your new analysis
91
+ distinguishes, not just real captured examples (useful for demos, too
92
+ brittle/large to be the actual test).
93
+
94
+ ## Running tests
95
+
96
+ ```bash
97
+ uv sync
98
+ uv run pytest
99
+ ```
100
+
101
+ `tests/adapter/` and `tests/analyzer/` run independently — a change to
102
+ one package's tests should never need to touch the other's.
103
+
104
+ ## Releasing to PyPI
105
+
106
+ Publishing uses [Trusted Publishing](https://docs.pypi.org/trusted-publishers/)
107
+ (OIDC) via GitHub Actions — no API token is stored anywhere. To cut a release:
108
+
109
+ 1. Bump `version` in `pyproject.toml`, merge that through the normal PR
110
+ workflow.
111
+ 2. Tag the merge commit `vX.Y.Z` and push the tag, or create a GitHub
112
+ Release directly against `main` with that tag.
113
+ 3. Publishing a GitHub Release triggers `.github/workflows/publish.yml`,
114
+ which builds the sdist/wheel with `uv build` and publishes them via
115
+ `pypa/gh-action-pypi-publish`.
116
+
117
+ The PyPI project must have this repository registered as a trusted
118
+ publisher first (PyPI account/org → Publishing settings → project name
119
+ `redundo`, repo `CogentWizards/redundo`, workflow `publish.yml`,
120
+ environment `pypi`) — a one-time setup step, done on pypi.org, not in
121
+ this repo.
122
+
123
+ ## Reporting a documented behavior that turned out to be wrong
124
+
125
+ If you've captured real telemetry from a source and it disagrees with
126
+ what's written in `docs/`, that's a genuinely valuable bug report even
127
+ without a fix attached — open an issue (the bug report template) with
128
+ the captured data (redacted of any real content) or a description of
129
+ the discrepancy.
redundo-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cogent AI
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.