Commit b2f4bb7
committed
feat(delphi): D11 — Clojure-parity consensus comment selection (PR 9)
Replaces the pre-D11 consensus logic (per-group `pa > 0.6 for all` filter,
top 2 by `avg_pa`) with a whole-conversation per-comment-stats stage and
two independent top-5 lists (agree / disagree), matching Clojure
`consensus-stats` + `select-consensus-comments`
(math/src/polismath/math/repness.clj:284-323).
Sits on top of PR 8 (D10) in the spr stack.
## Production changes (`repness.py`)
- **`consensus_stats_df(vote_matrix_df, mod_out=None) -> pd.DataFrame`**:
new helper. Whole-conversation per-comment stats (no group split). Output
DataFrame indexed by tid with cols [na, nd, ns, pa, pd, pat, pdt].
Vectorized port of Clojure `consensus-stats` (repness.clj:284-290).
**`ns` includes PASS** (Clojure parity, repness.clj:56-61): computed as
`vote_matrix_df.notna().sum(axis=0)` rather than `na + nd`. Matches
Clojure `(count (filter identity ...))` where `0` (PASS) is truthy.
- **`select_consensus_comments_df` rewrite**: new signature
`(cons_stats) -> Dict[str, List[Dict]]`. Filters and ordering:
- agree: `pa > 0.5 AND z-sig-90(pat)`, sorted desc by `am = pa * pat`.
- disagree: `pd > 0.5 AND z-sig-90(pdt)`, sorted desc by `dm = pd * pdt`.
Cap: top 5 each side. Output: `{'agree': [...], 'disagree': [...]}`.
With PSEUDO_COUNT=2, `pa + pd = 1` exact → `pa > 0.5 ⟺ pd < 0.5`, so
the same tid cannot appear in both lists.
- **`conv_repness` grows `mod_out` kwarg**, forwarded to both
`select_rep_comments_df` and `consensus_stats_df`. Consensus is now run
unconditionally — Clojure has no `len(groups) > 1` guard.
- **`_stats_row_to_dict` deleted** — orphan after D11.
## Caller (`conversation.py`)
`_compute_repness` passes `mod_out=self.mod_out_tids` to `conv_repness`,
matching Clojure's mod-out propagation (repness.clj:222 and :296).
## Downstream output shape change
`conv.repness['consensus_comments']` was a flat list with
`{repful: 'consensus', comment_id, avg_agree, stats}` entries. After D11
it's `{'agree': [entries], 'disagree': [entries]}` matching Clojure's
math-blob shape. Each entry has Python-convention keys (decision S1):
`{comment_id, n_success, n_trials, p_success, p_test}`.
Updated consumers in the test suite:
- `tests/test_repness_smoke.py::test_repness_structure` — iterates the
new dict shape.
- `tests/test_pipeline_integrity.py::test_full_pipeline` — same.
External downstream consumers (`client-report/normalizeConsensus.js`) may
need a parallel update; flagged in the decisions log for batch review.
## Tests (13 new in `tests/test_discrepancy_fixes.py`)
- `TestD11ConsensusStatsDf` (5): basic counts, pseudocount pa/pd
smoothing, ns=0 uninformative fallback, mod_out tid filter,
**ns-includes-PASS Clojure parity**.
- `TestD11SelectConsensusBoundary` (8): empty input, clear agree
consensus, clear disagree consensus, divisive (no consensus), top-5
cap, entry-key shape, disagree-side key mapping (n_success ← nd,
p_success ← pd, p_test ← pdt), mutually-exclusive agree/disagree lists.
## `ns`-PASS fix (Clojure parity)
After D11 was first landed (PR #2567), the real-data test
`test_consensus_matches_clojure` showed 3-5/5 overlap on cold_start.
Investigation revealed that Clojure's `:ns` (via `count-votes` with
`filter identity` — repness.clj:56-61) INCLUDES PASS votes (`0` is truthy
in Clojure), while Python's `ns = na + nd` excluded them.
Fixed here by switching `consensus_stats_df` to count via
`vote_matrix_df.notna().sum(axis=0)`. After the fix, 3 of 4 dataset
variants (vw-incremental, vw-cold_start, biodiversity-cold_start) match
Clojure exactly. The `biodiversity-incremental` variant still mismatches
on the disagree side (likely residual upstream PCA/KMeans group-membership
divergence) — remains `xfail(strict=False)` and tracked in the journal.
(The companion fix for `compute_group_comment_stats_df` ships in a
separate pre-D10 commit `qyskkqkovtmn`.)
## B1 + B2 sub-agent fixes (relocated from D12 per batch review 2026-06-11)
These two fixes were originally landed in PR #2568 (D12) because the D11
sub-agent review happened AFTER D11 had been pushed. They belong in D11,
so they are squashed into this commit:
- **B1** (`polismath/conversation/conversation.py:830-837`): the
no-groups branch of `_compute_repness` returned
`'consensus_comments': []` (list). After D11, the public shape is the
dict `{'agree': [...], 'disagree': [...]}`. Downstream consumers
(test_repness_smoke, test_pipeline_integrity) iterate the dict shape
and would crash on the legacy list. Fixed to always return the dict
shape, even with no groups.
- **B2** (`tests/test_legacy_repness_comparison.py:197-205`): the
legacy-comparison test extracted `py_consensus = py_results.get(
'consensus_comments', [])` and treated it as a flat list. Post-D11
this is a dict, so the ID extraction silently produced an empty set.
Fixed to flatten the dict (agree + disagree) for the legacy comparison,
with a `legacy fallback` branch in case the value is still a list.
## Suite delta
- Pre (post-D10): 313 passed, 12 skipped, 58 xfailed.
- Post (this PR): 330 passed, 12 skipped, 55 xfailed, 3 xpassed.
- Delta: +17 passed, -3 xfailed (D11 real-data test now passes on 3 of
4 dataset variants; biodiversity-incremental remains
xfail(strict=False)). Zero regressions.
## /goal mode
Part of an autonomous stacked PR series (D10 + D11 + D12 + goldens) per
user request. Decisions are documented for batch review in
`~/polis/D10_D11_D12_GOLDENS_DECISIONS.md`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
commit-id:bf7eabfe1 parent 9fc57e4 commit b2f4bb7
10 files changed
Lines changed: 780 additions & 108 deletions
File tree
- delphi
- docs
- polismath
- conversation
- database
- pca_kmeans_rep
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1506 | 1506 | | |
1507 | 1507 | | |
1508 | 1508 | | |
| 1509 | + | |
| 1510 | + | |
| 1511 | + | |
| 1512 | + | |
| 1513 | + | |
| 1514 | + | |
| 1515 | + | |
| 1516 | + | |
| 1517 | + | |
| 1518 | + | |
| 1519 | + | |
| 1520 | + | |
| 1521 | + | |
| 1522 | + | |
| 1523 | + | |
| 1524 | + | |
| 1525 | + | |
| 1526 | + | |
| 1527 | + | |
| 1528 | + | |
| 1529 | + | |
| 1530 | + | |
| 1531 | + | |
| 1532 | + | |
| 1533 | + | |
| 1534 | + | |
| 1535 | + | |
| 1536 | + | |
| 1537 | + | |
| 1538 | + | |
| 1539 | + | |
| 1540 | + | |
| 1541 | + | |
| 1542 | + | |
| 1543 | + | |
| 1544 | + | |
| 1545 | + | |
| 1546 | + | |
| 1547 | + | |
| 1548 | + | |
| 1549 | + | |
| 1550 | + | |
| 1551 | + | |
| 1552 | + | |
| 1553 | + | |
| 1554 | + | |
| 1555 | + | |
| 1556 | + | |
| 1557 | + | |
| 1558 | + | |
| 1559 | + | |
| 1560 | + | |
| 1561 | + | |
| 1562 | + | |
| 1563 | + | |
| 1564 | + | |
| 1565 | + | |
| 1566 | + | |
| 1567 | + | |
| 1568 | + | |
| 1569 | + | |
| 1570 | + | |
| 1571 | + | |
| 1572 | + | |
| 1573 | + | |
| 1574 | + | |
| 1575 | + | |
| 1576 | + | |
| 1577 | + | |
| 1578 | + | |
| 1579 | + | |
| 1580 | + | |
| 1581 | + | |
| 1582 | + | |
| 1583 | + | |
| 1584 | + | |
| 1585 | + | |
| 1586 | + | |
| 1587 | + | |
| 1588 | + | |
| 1589 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | | - | |
33 | | - | |
34 | | - | |
| 32 | + | |
| 33 | + | |
35 | 34 | | |
36 | 35 | | |
37 | 36 | | |
| |||
916 | 915 | | |
917 | 916 | | |
918 | 917 | | |
| 918 | + | |
| 919 | + | |
| 920 | + | |
| 921 | + | |
| 922 | + | |
| 923 | + | |
| 924 | + | |
| 925 | + | |
| 926 | + | |
| 927 | + | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
919 | 934 | | |
920 | 935 | | |
921 | 936 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
753 | 753 | | |
754 | 754 | | |
755 | 755 | | |
| 756 | + | |
| 757 | + | |
756 | 758 | | |
757 | 759 | | |
758 | 760 | | |
759 | | - | |
| 761 | + | |
760 | 762 | | |
761 | 763 | | |
762 | 764 | | |
763 | 765 | | |
764 | | - | |
765 | | - | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
766 | 772 | | |
767 | 773 | | |
768 | 774 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
300 | 300 | | |
301 | 301 | | |
302 | 302 | | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
303 | 310 | | |
304 | 311 | | |
305 | 312 | | |
| |||
308 | 315 | | |
309 | 316 | | |
310 | 317 | | |
311 | | - | |
| 318 | + | |
312 | 319 | | |
313 | 320 | | |
314 | 321 | | |
| |||
321 | 328 | | |
322 | 329 | | |
323 | 330 | | |
324 | | - | |
325 | | - | |
326 | | - | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
327 | 344 | | |
328 | | - | |
| 345 | + | |
329 | 346 | | |
330 | 347 | | |
331 | 348 | | |
| |||
840 | 857 | | |
841 | 858 | | |
842 | 859 | | |
843 | | - | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
844 | 867 | | |
845 | 868 | | |
846 | 869 | | |
| |||
0 commit comments