chorus-discourse 0.3.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.
- chorus_discourse-0.3.1/CHANGELOG.md +46 -0
- chorus_discourse-0.3.1/LICENSE +110 -0
- chorus_discourse-0.3.1/MANIFEST.in +2 -0
- chorus_discourse-0.3.1/PKG-INFO +135 -0
- chorus_discourse-0.3.1/README.md +125 -0
- chorus_discourse-0.3.1/docs/releases/0.3.0.md +64 -0
- chorus_discourse-0.3.1/pyproject.toml +29 -0
- chorus_discourse-0.3.1/setup.cfg +4 -0
- chorus_discourse-0.3.1/src/chorus/__init__.py +10 -0
- chorus_discourse-0.3.1/src/chorus/__main__.py +5 -0
- chorus_discourse-0.3.1/src/chorus/cli.py +223 -0
- chorus_discourse-0.3.1/src/chorus/corpora.py +71 -0
- chorus_discourse-0.3.1/src/chorus/daemon.py +146 -0
- chorus_discourse-0.3.1/src/chorus/decision.py +648 -0
- chorus_discourse-0.3.1/src/chorus/flagship.py +76 -0
- chorus_discourse-0.3.1/src/chorus/item.py +65 -0
- chorus_discourse-0.3.1/src/chorus/mcp.py +168 -0
- chorus_discourse-0.3.1/src/chorus/model.py +32 -0
- chorus_discourse-0.3.1/src/chorus/receipt.py +178 -0
- chorus_discourse-0.3.1/src/chorus/sentiment.py +135 -0
- chorus_discourse-0.3.1/src/chorus/synthesize.py +311 -0
- chorus_discourse-0.3.1/src/chorus_discourse.egg-info/PKG-INFO +135 -0
- chorus_discourse-0.3.1/src/chorus_discourse.egg-info/SOURCES.txt +37 -0
- chorus_discourse-0.3.1/src/chorus_discourse.egg-info/dependency_links.txt +1 -0
- chorus_discourse-0.3.1/src/chorus_discourse.egg-info/entry_points.txt +2 -0
- chorus_discourse-0.3.1/src/chorus_discourse.egg-info/top_level.txt +1 -0
- chorus_discourse-0.3.1/tests/test_cli.py +150 -0
- chorus_discourse-0.3.1/tests/test_cluster.py +33 -0
- chorus_discourse-0.3.1/tests/test_corpora.py +47 -0
- chorus_discourse-0.3.1/tests/test_daemon.py +119 -0
- chorus_discourse-0.3.1/tests/test_decision.py +431 -0
- chorus_discourse-0.3.1/tests/test_item.py +24 -0
- chorus_discourse-0.3.1/tests/test_mcp.py +138 -0
- chorus_discourse-0.3.1/tests/test_model_pass.py +93 -0
- chorus_discourse-0.3.1/tests/test_receipt.py +215 -0
- chorus_discourse-0.3.1/tests/test_repo_art.py +272 -0
- chorus_discourse-0.3.1/tests/test_sentiment.py +39 -0
- chorus_discourse-0.3.1/tests/test_synthesize.py +152 -0
- chorus_discourse-0.3.1/tests/test_version_alignment.py +50 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.1 - 2026-09-22
|
|
4
|
+
|
|
5
|
+
- Fixes the gather-to-chorus wiring gap. `normalize()` mapped only `feed_item`,
|
|
6
|
+
so feed entries from a real gather corpus were dropped without a diagnostic and
|
|
7
|
+
only YouTube comments reached the discourse surface. `feed-entry` now maps to
|
|
8
|
+
the `feed` source, and `feed_item` is kept for back-compat.
|
|
9
|
+
- Adds an OIDC trusted-publishing release workflow with tag/version, artifact
|
|
10
|
+
digest, clean-venv entry-point resolution, and sdist-rebuild gates.
|
|
11
|
+
- Publishes to PyPI as `chorus-discourse`. The console script stays `chorus`.
|
|
12
|
+
|
|
13
|
+
## 0.3.0 - 2026-09-07
|
|
14
|
+
|
|
15
|
+
- Adds `chorus decision` and MCP `chorus.decision`, a source-change review gate
|
|
16
|
+
that compares current and reference gather-style source packs, reports
|
|
17
|
+
added/removed/changed/unchanged item ids, verifies both deterministic Chorus
|
|
18
|
+
digests, and returns typed `UNVERIFIABLE` failure states for missing, malformed,
|
|
19
|
+
empty, or ambiguous source inputs.
|
|
20
|
+
- Adds a safe public projection for the review-gate result. It keeps counts,
|
|
21
|
+
hashes, receipts, and limitations by default; human-readable public ids, source
|
|
22
|
+
names, refs, and URLs require an operator-authored projection policy rather
|
|
23
|
+
than source-row metadata. It excludes raw source text, author names, local paths,
|
|
24
|
+
private sessions, and bulk comments.
|
|
25
|
+
- Hardens false-success controls for source packs: missing or malformed text,
|
|
26
|
+
invalid, missing, mismatched, or non-UTF-8 Gather objects, duplicate ids, and
|
|
27
|
+
absent, null, blank, or non-string ids return typed `UNVERIFIABLE` instead of
|
|
28
|
+
being compared as unchanged.
|
|
29
|
+
|
|
30
|
+
This release does not decide whether a source claim is true, complete, or ready
|
|
31
|
+
for publication. `MATCH` means the compared source observations and fingerprints
|
|
32
|
+
are unchanged; `DRIFT` means a reviewer should inspect the changed source rows;
|
|
33
|
+
`UNVERIFIABLE` means source capture or identity must be repaired before reuse.
|
|
34
|
+
|
|
35
|
+
## 0.2.0 - 2026-09-07
|
|
36
|
+
|
|
37
|
+
- Adds deterministic corpus-salience theme labels with `label_quality` support metadata so weak and singleton labels are visible in the digest.
|
|
38
|
+
- Writes new receipts as `chorus-lens/3`, binding label terms and `label_quality` into the re-checkable digest body.
|
|
39
|
+
- Preserves historical `chorus-lens/2` verification through an explicit legacy verifier. v2 receipts verify against the v2 body shape and do not bind the current v3-only `terms` and `label_quality` fields.
|
|
40
|
+
- Fixes receipt verification so the submitted digest body must match its receipt before the verifier accepts a re-derived body.
|
|
41
|
+
|
|
42
|
+
This release does not claim better semantic insight from comment corpora. It reduces generic-label false success on the included controls and labels weak support, while lexical clustering remains a known limitation.
|
|
43
|
+
|
|
44
|
+
## 0.1.0
|
|
45
|
+
|
|
46
|
+
- Initial discourse synthesis package with CLI, MCP stdio surface, daemon, deterministic sentiment, lexical clustering, contested aspects, and re-checkable receipts.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, MIT Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-MIT
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 Zain Dana Harper
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the MIT license that is effective on the second anniversary of the date we make
|
|
91
|
+
the Software available. On or after that date, you may use the Software under
|
|
92
|
+
the MIT license, in which case the following will apply:
|
|
93
|
+
|
|
94
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
95
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
96
|
+
the Software without restriction, including without limitation the rights to
|
|
97
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
98
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
99
|
+
so, subject to the following conditions:
|
|
100
|
+
|
|
101
|
+
The above copyright notice and this permission notice shall be included in all
|
|
102
|
+
copies or substantial portions of the Software.
|
|
103
|
+
|
|
104
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
105
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
106
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
107
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
108
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
109
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
110
|
+
SOFTWARE.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chorus-discourse
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: A discourse-synthesis satellite for gather: weighted, clustered, re-checkable readings of comment corpora.
|
|
5
|
+
License-Expression: LicenseRef-FSL-1.1-MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Dynamic: license-file
|
|
10
|
+
|
|
11
|
+
# chorus
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
Read a comment section the way you wish you could: not scrolled, but **synthesized**.
|
|
16
|
+
|
|
17
|
+
chorus takes a corpus of comments or threads and returns a weighted, clustered,
|
|
18
|
+
re-checkable reading of the discourse. It tells you the themes people are actually
|
|
19
|
+
voicing, ranks them by how much the crowd engaged and how strongly they felt,
|
|
20
|
+
surfaces the sharpest dissent instead of hiding it behind an average, and names the
|
|
21
|
+
topics the crowd is genuinely **split** on. Every digest carries a receipt a
|
|
22
|
+
stranger can re-run to get the same answer. Zero third-party runtime dependencies.
|
|
23
|
+
|
|
24
|
+
It orbits [gather](https://github.com/HarperZ9/gather): gather captures the corpus
|
|
25
|
+
with provenance, chorus synthesizes the discourse on top of it.
|
|
26
|
+
|
|
27
|
+

|
|
28
|
+
|
|
29
|
+
## What you get
|
|
30
|
+
|
|
31
|
+
- **Themes, ranked.** Comments cluster into themes by what they say; each theme
|
|
32
|
+
carries a corpus-salience label, label-support metadata, its size, an
|
|
33
|
+
engagement-and-sentiment weight, a sentiment split, a *controversy* score (how
|
|
34
|
+
divided and how strongly felt), and the single highest-weight voice that
|
|
35
|
+
disagrees with the majority. Singleton and weak-support labels are named as
|
|
36
|
+
such in the digest instead of being presented as broad crowd themes. Theme item
|
|
37
|
+
IDs stay in the output so callers can resolve labels back to the source links
|
|
38
|
+
and provenance in their corpus.
|
|
39
|
+
- **The contested topics, named.** A separate lens reports the aspects the corpus
|
|
40
|
+
is genuinely split on, measured across *every* comment that mentions a topic. It
|
|
41
|
+
is immune to the lexical clustering that would otherwise file "the battery is
|
|
42
|
+
amazing" and "the battery is terrible" under different themes and hide the fight.
|
|
43
|
+
One-sided praise and neutral chatter are excluded; only real two-sided
|
|
44
|
+
disagreement is surfaced.
|
|
45
|
+
- **A receipt, not a vibe.** `--verify` re-derives the whole digest from the inputs
|
|
46
|
+
and confirms it. A tampered digest fails even if its own hash was recomputed to
|
|
47
|
+
match. Sentiment is a *weight*, never a verdict.
|
|
48
|
+
- **Honest nulls.** A missing engagement signal is recorded as absent, never
|
|
49
|
+
counted as a zero. A too-thin corpus says so. The lexicon's limits (English-only,
|
|
50
|
+
literal, no sarcasm) are stated in the digest itself.
|
|
51
|
+
- **A daemon.** Point it at a watchlist and it re-synthesizes only when a corpus
|
|
52
|
+
actually changes, storing each receipted digest by its own hash.
|
|
53
|
+
- **A source-change review gate.** Compare a current gather corpus against a
|
|
54
|
+
reference corpus before reusing a prior synthesis or release note. The result
|
|
55
|
+
reports added, removed, changed, unchanged, digest verification, a local digest
|
|
56
|
+
outline, and typed source failure states. The optional public projection is
|
|
57
|
+
safe by default as hashes and counts. Human-readable public ids, source names,
|
|
58
|
+
refs, and URLs require an operator-authored `--public-policy` sidecar; raw
|
|
59
|
+
source row metadata cannot authorize public output. The projection omits raw
|
|
60
|
+
source text, author names, local paths, private session content, and bulk
|
|
61
|
+
comments. Source URLs appear there only through that public-policy sidecar.
|
|
62
|
+
- **An MCP surface.** Drive it from any MCP host: `chorus.run`, `chorus.corpora`,
|
|
63
|
+
`chorus.digests`, `chorus.decision`, `chorus.status`, `chorus.doctor`.
|
|
64
|
+
|
|
65
|
+
## Release notes
|
|
66
|
+
|
|
67
|
+
See [CHANGELOG.md](CHANGELOG.md). Version 0.3.0 adds `chorus decision` and MCP `chorus.decision` as a source-change review gate for deciding whether a prior synthesis or release note can reuse the same source observations, needs source review, or must hold for source repair. It does not decide whether a source claim is true, complete, or ready for publication. Version 0.2.0 writes `chorus-lens/3` receipts with label-support metadata while preserving historical `chorus-lens/2` verification through an explicit legacy path. v2 receipts do not bind the current v3-only label terms or `label_quality` fields.
|
|
68
|
+
|
|
69
|
+
## Run it
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install -e .
|
|
73
|
+
|
|
74
|
+
chorus run examples/discourse-sample.json --verify # try it on the bundled sample
|
|
75
|
+
chorus run <corpus> --verify # a corpus -> a verified discourse digest
|
|
76
|
+
chorus decision <current> --reference <reference> --task "Check whether sources changed"
|
|
77
|
+
chorus decision <current> --reference <reference> --public --public-policy public-policy.json
|
|
78
|
+
chorus corpora <root> # discover gather corpora as discourse sources
|
|
79
|
+
chorus watch add <corpus> # add a corpus to the daemon watchlist
|
|
80
|
+
chorus daemon --interval 300 # poll the watchlist, synthesize on change
|
|
81
|
+
chorus digests <store> # what the daemon has synthesized
|
|
82
|
+
chorus mcp # the MCP stdio server
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`<corpus>` is a gather corpus directory (a folder holding `catalog.jsonl`) or a
|
|
86
|
+
JSON list of rows. Add `--model "<command>"` to `run` to overlay a model's read on
|
|
87
|
+
the comments the lexicon is least sure about; the overlay is provenance-tagged and
|
|
88
|
+
never enters the re-checkable core.
|
|
89
|
+
|
|
90
|
+
No service key or provider account is needed for the deterministic path. The
|
|
91
|
+
bundled sample is the quickest local check:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
chorus run examples/discourse-sample.json --verify
|
|
95
|
+
python -m pytest
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
If `chorus` is not on `PATH`, reinstall from the checkout with
|
|
99
|
+
`python -m pip install -e .` and open a new shell. `chorus.status` and
|
|
100
|
+
`chorus.doctor` are MCP tools exposed through `chorus mcp`; they are not separate
|
|
101
|
+
CLI subcommands. Public source-change projections need an operator-authored
|
|
102
|
+
`--public-policy` sidecar before source names, ids, refs, or URLs appear.
|
|
103
|
+
|
|
104
|
+

|
|
105
|
+
|
|
106
|
+
## The receipt
|
|
107
|
+
|
|
108
|
+
The digest's `receipt` binds the inputs, the method, and the result. `verify`
|
|
109
|
+
first checks that the submitted digest body still matches its own receipt, then
|
|
110
|
+
re-runs the deterministic pipeline (score, cluster, weight) from the same corpus
|
|
111
|
+
through the method-version verifier recorded in the receipt. Current runs write
|
|
112
|
+
`chorus-lens/3`; historical `chorus-lens/2` receipts remain checkable through
|
|
113
|
+
their legacy label body. Unsupported method versions fail closed. Any model
|
|
114
|
+
overlay is listed separately with its own provenance and is excluded from that
|
|
115
|
+
check: the parts a stranger can re-derive and the parts that are model opinion
|
|
116
|
+
are kept distinct, on the record.
|
|
117
|
+
|
|
118
|
+

|
|
119
|
+
|
|
120
|
+
## Design
|
|
121
|
+
|
|
122
|
+
The design and its two exposed defects-caught-in-review live in
|
|
123
|
+
[docs/superpowers/specs](docs/superpowers/specs). Sentiment is coarse by
|
|
124
|
+
construction and the digest says so; clustering is lexical, not semantic. chorus
|
|
125
|
+
tells you what it did and hands you the means to check it.
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
Source-available under the Functional Source License (FSL-1.1-MIT) (see [LICENSE](LICENSE)):
|
|
130
|
+
read it, run it, build on it; commercial use that competes with the project is
|
|
131
|
+
reserved.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
**[Zentropy Labs](https://github.com/ZentropyLabs-ai)** · order out of entropy. An independent lab building evidence-first tools that leave a re-checkable artifact behind. Built by Zain Dana Harper in Seattle. The full workbench is at [Project Telos](https://harperz9.github.io).
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# chorus
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Read a comment section the way you wish you could: not scrolled, but **synthesized**.
|
|
6
|
+
|
|
7
|
+
chorus takes a corpus of comments or threads and returns a weighted, clustered,
|
|
8
|
+
re-checkable reading of the discourse. It tells you the themes people are actually
|
|
9
|
+
voicing, ranks them by how much the crowd engaged and how strongly they felt,
|
|
10
|
+
surfaces the sharpest dissent instead of hiding it behind an average, and names the
|
|
11
|
+
topics the crowd is genuinely **split** on. Every digest carries a receipt a
|
|
12
|
+
stranger can re-run to get the same answer. Zero third-party runtime dependencies.
|
|
13
|
+
|
|
14
|
+
It orbits [gather](https://github.com/HarperZ9/gather): gather captures the corpus
|
|
15
|
+
with provenance, chorus synthesizes the discourse on top of it.
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
## What you get
|
|
20
|
+
|
|
21
|
+
- **Themes, ranked.** Comments cluster into themes by what they say; each theme
|
|
22
|
+
carries a corpus-salience label, label-support metadata, its size, an
|
|
23
|
+
engagement-and-sentiment weight, a sentiment split, a *controversy* score (how
|
|
24
|
+
divided and how strongly felt), and the single highest-weight voice that
|
|
25
|
+
disagrees with the majority. Singleton and weak-support labels are named as
|
|
26
|
+
such in the digest instead of being presented as broad crowd themes. Theme item
|
|
27
|
+
IDs stay in the output so callers can resolve labels back to the source links
|
|
28
|
+
and provenance in their corpus.
|
|
29
|
+
- **The contested topics, named.** A separate lens reports the aspects the corpus
|
|
30
|
+
is genuinely split on, measured across *every* comment that mentions a topic. It
|
|
31
|
+
is immune to the lexical clustering that would otherwise file "the battery is
|
|
32
|
+
amazing" and "the battery is terrible" under different themes and hide the fight.
|
|
33
|
+
One-sided praise and neutral chatter are excluded; only real two-sided
|
|
34
|
+
disagreement is surfaced.
|
|
35
|
+
- **A receipt, not a vibe.** `--verify` re-derives the whole digest from the inputs
|
|
36
|
+
and confirms it. A tampered digest fails even if its own hash was recomputed to
|
|
37
|
+
match. Sentiment is a *weight*, never a verdict.
|
|
38
|
+
- **Honest nulls.** A missing engagement signal is recorded as absent, never
|
|
39
|
+
counted as a zero. A too-thin corpus says so. The lexicon's limits (English-only,
|
|
40
|
+
literal, no sarcasm) are stated in the digest itself.
|
|
41
|
+
- **A daemon.** Point it at a watchlist and it re-synthesizes only when a corpus
|
|
42
|
+
actually changes, storing each receipted digest by its own hash.
|
|
43
|
+
- **A source-change review gate.** Compare a current gather corpus against a
|
|
44
|
+
reference corpus before reusing a prior synthesis or release note. The result
|
|
45
|
+
reports added, removed, changed, unchanged, digest verification, a local digest
|
|
46
|
+
outline, and typed source failure states. The optional public projection is
|
|
47
|
+
safe by default as hashes and counts. Human-readable public ids, source names,
|
|
48
|
+
refs, and URLs require an operator-authored `--public-policy` sidecar; raw
|
|
49
|
+
source row metadata cannot authorize public output. The projection omits raw
|
|
50
|
+
source text, author names, local paths, private session content, and bulk
|
|
51
|
+
comments. Source URLs appear there only through that public-policy sidecar.
|
|
52
|
+
- **An MCP surface.** Drive it from any MCP host: `chorus.run`, `chorus.corpora`,
|
|
53
|
+
`chorus.digests`, `chorus.decision`, `chorus.status`, `chorus.doctor`.
|
|
54
|
+
|
|
55
|
+
## Release notes
|
|
56
|
+
|
|
57
|
+
See [CHANGELOG.md](CHANGELOG.md). Version 0.3.0 adds `chorus decision` and MCP `chorus.decision` as a source-change review gate for deciding whether a prior synthesis or release note can reuse the same source observations, needs source review, or must hold for source repair. It does not decide whether a source claim is true, complete, or ready for publication. Version 0.2.0 writes `chorus-lens/3` receipts with label-support metadata while preserving historical `chorus-lens/2` verification through an explicit legacy path. v2 receipts do not bind the current v3-only label terms or `label_quality` fields.
|
|
58
|
+
|
|
59
|
+
## Run it
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install -e .
|
|
63
|
+
|
|
64
|
+
chorus run examples/discourse-sample.json --verify # try it on the bundled sample
|
|
65
|
+
chorus run <corpus> --verify # a corpus -> a verified discourse digest
|
|
66
|
+
chorus decision <current> --reference <reference> --task "Check whether sources changed"
|
|
67
|
+
chorus decision <current> --reference <reference> --public --public-policy public-policy.json
|
|
68
|
+
chorus corpora <root> # discover gather corpora as discourse sources
|
|
69
|
+
chorus watch add <corpus> # add a corpus to the daemon watchlist
|
|
70
|
+
chorus daemon --interval 300 # poll the watchlist, synthesize on change
|
|
71
|
+
chorus digests <store> # what the daemon has synthesized
|
|
72
|
+
chorus mcp # the MCP stdio server
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`<corpus>` is a gather corpus directory (a folder holding `catalog.jsonl`) or a
|
|
76
|
+
JSON list of rows. Add `--model "<command>"` to `run` to overlay a model's read on
|
|
77
|
+
the comments the lexicon is least sure about; the overlay is provenance-tagged and
|
|
78
|
+
never enters the re-checkable core.
|
|
79
|
+
|
|
80
|
+
No service key or provider account is needed for the deterministic path. The
|
|
81
|
+
bundled sample is the quickest local check:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
chorus run examples/discourse-sample.json --verify
|
|
85
|
+
python -m pytest
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
If `chorus` is not on `PATH`, reinstall from the checkout with
|
|
89
|
+
`python -m pip install -e .` and open a new shell. `chorus.status` and
|
|
90
|
+
`chorus.doctor` are MCP tools exposed through `chorus mcp`; they are not separate
|
|
91
|
+
CLI subcommands. Public source-change projections need an operator-authored
|
|
92
|
+
`--public-policy` sidecar before source names, ids, refs, or URLs appear.
|
|
93
|
+
|
|
94
|
+

|
|
95
|
+
|
|
96
|
+
## The receipt
|
|
97
|
+
|
|
98
|
+
The digest's `receipt` binds the inputs, the method, and the result. `verify`
|
|
99
|
+
first checks that the submitted digest body still matches its own receipt, then
|
|
100
|
+
re-runs the deterministic pipeline (score, cluster, weight) from the same corpus
|
|
101
|
+
through the method-version verifier recorded in the receipt. Current runs write
|
|
102
|
+
`chorus-lens/3`; historical `chorus-lens/2` receipts remain checkable through
|
|
103
|
+
their legacy label body. Unsupported method versions fail closed. Any model
|
|
104
|
+
overlay is listed separately with its own provenance and is excluded from that
|
|
105
|
+
check: the parts a stranger can re-derive and the parts that are model opinion
|
|
106
|
+
are kept distinct, on the record.
|
|
107
|
+
|
|
108
|
+

|
|
109
|
+
|
|
110
|
+
## Design
|
|
111
|
+
|
|
112
|
+
The design and its two exposed defects-caught-in-review live in
|
|
113
|
+
[docs/superpowers/specs](docs/superpowers/specs). Sentiment is coarse by
|
|
114
|
+
construction and the digest says so; clustering is lexical, not semantic. chorus
|
|
115
|
+
tells you what it did and hands you the means to check it.
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
Source-available under the Functional Source License (FSL-1.1-MIT) (see [LICENSE](LICENSE)):
|
|
120
|
+
read it, run it, build on it; commercial use that competes with the project is
|
|
121
|
+
reserved.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
**[Zentropy Labs](https://github.com/ZentropyLabs-ai)** · order out of entropy. An independent lab building evidence-first tools that leave a re-checkable artifact behind. Built by Zain Dana Harper in Seattle. The full workbench is at [Project Telos](https://harperz9.github.io).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Chorus 0.3.0
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-07
|
|
4
|
+
|
|
5
|
+
## Useful benefit
|
|
6
|
+
|
|
7
|
+
Chorus 0.3.0 adds a source-change review gate through the CLI command
|
|
8
|
+
`chorus decision` and the MCP tool `chorus.decision`. A reviewer can compare a
|
|
9
|
+
current Gather-style source pack against the reference pack used for a prior
|
|
10
|
+
synthesis or release note and get a machine-readable result:
|
|
11
|
+
|
|
12
|
+
- `MATCH`: the compared source observations and fingerprints are unchanged.
|
|
13
|
+
- `DRIFT`: one or more source rows were added, removed, or changed, so the prior
|
|
14
|
+
synthesis or release note needs source review before reuse.
|
|
15
|
+
- `UNVERIFIABLE`: the source pack is missing, malformed, internally inconsistent,
|
|
16
|
+
or has invalid identities, so reuse should hold until source capture is
|
|
17
|
+
repaired.
|
|
18
|
+
|
|
19
|
+
The public projection is safe by default as hashes, counts, checks, limitations,
|
|
20
|
+
and policy provenance. Human-readable public ids, source names, refs, and URLs
|
|
21
|
+
require an operator-authored public projection policy. Raw source row metadata
|
|
22
|
+
cannot publish those fields.
|
|
23
|
+
|
|
24
|
+
## Semantic limits
|
|
25
|
+
|
|
26
|
+
This release does not decide whether a source claim is true, complete, or ready
|
|
27
|
+
for publication. A `MATCH` result means the compared rows and fingerprints did not
|
|
28
|
+
change. It does not prove that the source set is representative, that no relevant
|
|
29
|
+
source exists elsewhere, that the implementation using the source is correct, or
|
|
30
|
+
that a publication should proceed. `DRIFT` is a review trigger. `UNVERIFIABLE` is
|
|
31
|
+
a source-repair trigger.
|
|
32
|
+
|
|
33
|
+
## False-success controls
|
|
34
|
+
|
|
35
|
+
Regression checks cover the observed failure modes:
|
|
36
|
+
|
|
37
|
+
- missing paths and malformed JSON return typed `UNVERIFIABLE` results;
|
|
38
|
+
- missing, blank, non-string, or null source text returns typed `UNVERIFIABLE`;
|
|
39
|
+
- invalid, missing, mismatched, or non-UTF-8 Gather content objects return typed
|
|
40
|
+
`UNVERIFIABLE`;
|
|
41
|
+
- duplicate ids and absent, null, blank, bool, numeric, list, or dict ids return
|
|
42
|
+
typed `UNVERIFIABLE` before normalization can coerce them;
|
|
43
|
+
- source-controlled row metadata cannot authorize public ids, source names,
|
|
44
|
+
refs, or URLs;
|
|
45
|
+
- operator public projection policy can publish safe human-readable fields when
|
|
46
|
+
supplied deliberately by the caller.
|
|
47
|
+
|
|
48
|
+
## Install and use
|
|
49
|
+
|
|
50
|
+
Download the wheel and checksums from the GitHub release. With Python 3.10 or
|
|
51
|
+
later, install the downloaded file and inspect the command's input options:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
python -m pip install ./chorus_discourse-0.3.0-py3-none-any.whl
|
|
55
|
+
chorus decision --help
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The package has no runtime dependencies. Existing MCP hosts can call
|
|
59
|
+
`chorus.decision` through `chorus mcp`. Keep input packs private unless they were
|
|
60
|
+
deliberately prepared for publication. A public policy authorizes only its
|
|
61
|
+
selected metadata; it does not authorize publishing raw source corpora.
|
|
62
|
+
|
|
63
|
+
GitHub downloads, host installation, and third-party marketplace approval are
|
|
64
|
+
separate distribution states. This release makes no marketplace approval claim.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "chorus-discourse"
|
|
7
|
+
version = "0.3.1"
|
|
8
|
+
description = "A discourse-synthesis satellite for gather: weighted, clustered, re-checkable readings of comment corpora."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "LicenseRef-FSL-1.1-MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
dependencies = []
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
chorus = "chorus.cli:main"
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.packages.find]
|
|
19
|
+
where = ["src"]
|
|
20
|
+
|
|
21
|
+
[tool.pytest.ini_options]
|
|
22
|
+
# Without this, pytest imports whatever copy of the package happens to be
|
|
23
|
+
# installed in the environment instead of this repo's src/. That is not a
|
|
24
|
+
# style preference: chorus was resolving to a stale editable install pointing
|
|
25
|
+
# at an old worktree, and plexus to a copy in site-packages, so both suites
|
|
26
|
+
# were green while testing source that was not this checkout.
|
|
27
|
+
pythonpath = ["src"]
|
|
28
|
+
testpaths = ["tests"]
|
|
29
|
+
addopts = "-q"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""chorus — a discourse-synthesis satellite for gather.
|
|
2
|
+
|
|
3
|
+
Turns a corpus of comments and threads into a weighted, clustered, re-checkable
|
|
4
|
+
reading of the discourse. Sentiment is a weight and a signal here, never an
|
|
5
|
+
accept gate. Stdlib only; deterministic; every digest carries a receipt.
|
|
6
|
+
"""
|
|
7
|
+
from chorus.item import DiscourseItem, normalize
|
|
8
|
+
|
|
9
|
+
__version__ = "0.3.1"
|
|
10
|
+
__all__ = ["DiscourseItem", "normalize"]
|