Skip to content

Promote edge to stable: remove POST participants, delete unused toxicity code, console refusal message, full CLI client - #3089

Merged
colinmegill merged 11 commits into
stablefrom
edge
Oct 17, 2026
Merged

colinmegill merged 11 commits into
stablefrom
edge

Conversation

@colinmegill

Copy link
Copy Markdown
Member

Server: POST /api/v3/participants removed (404; it admitted people past invite-only and external-id gates); the unused comment toxicity code deleted (no behaviour change); the admin console explains a refused setting change. Also the full CLI client (join, vote, comment, setup wizard, admin commands, topic agenda) and the Node-vs-Rust CLI comparison. No schema change.

colinmegill and others added 11 commits October 16, 2026 12:58
The moderation test seeded its conversation with the helper's default
account, which is not the conversation's owner or on the owner's site.
Seeds from such an account are now held for moderation like any other
statement, so the test seeds as the account that created the conversation,
as the neighbouring specs do. The seed helpers say so in their docs, and a
server test pins the owner path: an owner's bearer token creates a
conversation and seeds it, and the seeds are approved at once.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`polis join <conversation>` prepares anonymous participation the way the web
client does: it checks the conversation through participationInit and
reports whether this profile is already enrolled; the participant itself is
created by the first vote or comment, as on the web. `--invite CODE` accepts
an invite-tree invite and stores the returned login code; `--login-code`
signs a returning participant back in. Participant tokens and codes are kept
per profile, site and conversation in the owner-only credentials store and are
never printed. No server change.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
POST /api/v3/participants has no caller in any client, test helper, tool or
doc, and given any bearer token it created a participant past the
invite-only, external-id and closed-conversation gates; a signed-in account
could then vote in an invite-only conversation. The route and its handler
are removed; GET /api/v3/participants stays.

Observable change: POST /api/v3/participants answers 404 and writes nothing.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… module (#3084)

The three harness entry points waited for the comment moderation module to
load its prompt before starting. The comment path no longer uses that
check, so the wait is dropped; nothing the harnesses record changes. This
lands on its own so the module itself can be removed in a following change.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
When the server refuses a change on the Participant Management screen, the
console now shows a short line under that setting. For the external-id
allow-list once participants have joined it reads: "This setting can't
change once participants have joined. Start a new conversation to use a
different setting." Any other refusal reads "Couldn't save this change."
The checkbox keeps the server's value, as before.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`polis vote <conversation>` shows the next statement and records agree,
disagree or pass, then the next one, until the participant stops or nothing
is left. `--statement N --agree|--disagree|--pass` records one vote, for
scripts and agents, with --json output. In an open conversation no join step
is needed: the first vote creates the anonymous participant exactly as the
web client does, and its token is kept for the following votes. Every vote
value goes through the shared vote convention. No server change.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The comment toxicity check was switched off on the comment path. This
removes its now-unused code: the language-model moderation module and its
prompt, the profanity word list and its helper, and the moderation function
in the comment routes, along with the two packages only they used. The
report-narrative code keeps its own model client.

Observable change: none on the comment path; the server no longer loads the
moderation prompt at start-up.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`polis comment <conversation> "text"` submits a statement as the
participant. In an open conversation the first comment, like the first vote,
creates the anonymous participant the way the web client does, and its token
is kept; invited participants use the token from `polis join --invite`. The
command never sends the seed flag, and the submitted text is never echoed in
errors. No server change.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…de (#3086)

`polis conversation new` walks an owner through starting a conversation:
who can take part (open, anonymous, invite codes, external ids and their
allow-list, sign-in), what participants can do, moderation and seed
statements. `--from setup.json` applies the same typed setup for scripts and
agents, `--dry-run` explains what would happen, and the wizard can `--save`
the file. The setup is a typed model in polis-contracts in which combinations
the server would not honour cannot be expressed, with one mapping onto the
conversation settings. Applying runs in order (create, settings, invite
waves, allow-list, seed statements) and reads back after each step; a partial
failure reports exactly what was done. docs/conversation-setup.md describes
every option and the admission behaviour behind it. No server change.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* cli: a Node-vs-Rust comparison of every CLI command

scripts/test-polis-cli-differential.sh runs every CLI command against the
same fixture data twice, once with the Rust routes switched off (Node) and
once on, and requires identical --json data. It covers participant flows
(join, vote, comment) as well as admin reads, records which backend served
each request, and runs on its own in seconds, separate from the byte-level
recordings. Commands backed by a route that exists only in Rust are listed
separately: with Rust off they must fail cleanly as unavailable, and they
are judged by their own contract tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

* ci: keep the differential results inside the runner's temp directory

The upload step refuses a path that starts with '..'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…da (#3088)

Adds CLI commands for the remaining Node routes that an owner, moderator or
participant uses: moderation, conversation management, invites and
external-id lists, reports and exports, topics and Delphi results, and the
topic agenda (`polis agenda list | add | replace | remove | priorities`).
Each command calls an existing route, validates its input before any
request, reports through the CLI's exit codes, and is covered by the
Node-vs-Rust comparison. Unsafe legacy routes have no command (the removed
join routes), participant commands never send the seed flag, and the
external-id map download signs in as an owner or moderator. No server
change.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Delphi Coverage Report

File Stmts Miss Cover
polismath/init.py 2 0 100%
polismath/benchmarks/bench_pca.py 128 107 16%
polismath/benchmarks/bench_repness.py 81 65 20%
polismath/benchmarks/bench_update_votes.py 38 28 26%
polismath/benchmarks/benchmark_utils.py 34 0 100%
polismath/components/init.py 1 0 100%
polismath/components/config.py 165 70 58%
polismath/conversation/init.py 2 0 100%
polismath/conversation/conversation.py 1291 264 80%
polismath/conversation/group_vote_counts.py 38 2 95%
polismath/conversation/manager.py 131 42 68%
polismath/database/init.py 1 0 100%
polismath/database/dynamodb.py 392 176 55%
polismath/database/postgres.py 414 95 77%
polismath/delphi_storage/init.py 1 0 100%
polismath/delphi_storage/codec.py 295 22 93%
polismath/delphi_storage/embedding_spool.py 58 6 90%
polismath/delphi_storage/golden_corpus.py 60 9 85%
polismath/delphi_storage/import_report.py 51 3 94%
polismath/delphi_storage/legacy_import.py 171 16 91%
polismath/delphi_storage/postgres.py 49 22 55%
polismath/delphi_storage/production_import.py 612 131 79%
polismath/delphi_storage/resource.py 179 32 82%
polismath/delphi_storage/scan_pacing.py 77 0 100%
polismath/delphi_storage/writer.py 193 18 91%
polismath/empty_output.py 29 2 93%
polismath/engine_adapter.py 317 265 16%
polismath/job_child/init.py 356 52 85%
polismath/job_child/census.py 101 8 92%
polismath/pca_kmeans_rep/init.py 5 0 100%
polismath/pca_kmeans_rep/clusters.py 257 38 85%
polismath/pca_kmeans_rep/corr.py 98 17 83%
polismath/pca_kmeans_rep/group_k_smoother.py 28 0 100%
polismath/pca_kmeans_rep/legacy_kmeans.py 197 7 96%
polismath/pca_kmeans_rep/pca.py 162 19 88%
polismath/pca_kmeans_rep/repness.py 226 5 98%
polismath/poller/init.py 4 0 100%
polismath/poller/admission.py 318 15 95%
polismath/poller/backfill.py 936 32 97%
polismath/poller/capacity.py 485 27 94%
polismath/poller/capacity_queue.py 248 13 95%
polismath/poller/coordinator_bridge.py 317 317 0%
polismath/poller/math_writer.py 113 3 97%
polismath/poller/promotion.py 112 4 96%
polismath/poller/readiness.py 368 13 96%
polismath/poller/rebuild_child.py 133 5 96%
polismath/poller/service.py 665 52 92%
polismath/poller/worker_pool.py 140 0 100%
polismath/queue/executor.py 223 111 50%
polismath/regression/init.py 4 0 100%
polismath/regression/clojure_comparer.py 188 20 89%
polismath/regression/comparer.py 891 640 28%
polismath/regression/datasets.py 135 27 80%
polismath/regression/recorder.py 36 27 25%
polismath/regression/utils.py 130 85 35%
polismath/replay/init.py 2 0 100%
polismath/replay/attribution_capture.py 49 0 100%
polismath/replay/axis_continuity.py 347 24 93%
polismath/replay/certify.py 999 59 94%
polismath/replay/crosslang.py 128 2 98%
polismath/replay/decision_ties.py 55 8 85%
polismath/replay/diagnostics.py 50 7 86%
polismath/replay/driver.py 73 1 99%
polismath/replay/dsn_admission.py 36 1 97%
polismath/replay/equiv_query.py 10 0 100%
polismath/replay/event_ingress.py 95 1 99%
polismath/replay/fixture_bundle.py 815 62 92%
polismath/replay/fixture_config.py 266 11 96%
polismath/replay/fixture_extract.py 285 12 96%
polismath/replay/fixture_generate.py 103 1 99%
polismath/replay/fixture_samples.py 157 14 91%
polismath/replay/fixture_selection.py 119 0 100%
polismath/replay/fixture_survey.py 147 11 93%
polismath/replay/legacy_pca.py 29 17 41%
polismath/replay/polarity.py 319 25 92%
polismath/replay/poller_equiv.py 776 99 87%
polismath/replay/prodclone.py 193 2 99%
polismath/replay/real_data.py 146 10 93%
polismath/replay/schedule.py 205 10 95%
polismath/replay/served_math.py 159 4 97%
polismath/replay/shard_bench.py 189 65 66%
polismath/replay/stagecompare.py 890 161 82%
polismath/replay/stages.py 256 40 84%
polismath/replay/stepcompare.py 86 1 99%
polismath/replay/store.py 120 11 91%
polismath/replay/tie_capture.py 107 19 82%
polismath/replay/types.py 71 6 92%
polismath/run_math_pipeline.py 284 93 67%
polismath/tools/engine_rebuild.py 230 11 95%
polismath/types.py 22 0 100%
polismath/utils/cli_flags.py 15 0 100%
polismath/utils/clj_hash.py 44 0 100%
polismath/utils/env_flags.py 13 0 100%
polismath/utils/general.py 63 39 38%
polismath/utils/output_profile.py 66 2 97%
polismath/utils/serialization.py 10 5 50%
polismath/utils/vote_convention.py 159 5 97%
umap_narrative/501_calculate_comment_extremity.py 113 38 66%
umap_narrative/502_calculate_priorities.py 136 103 24%
umap_narrative/700_datamapplot_for_layer.py 507 507 0%
umap_narrative/701_static_datamapplot_for_layer.py 310 310 0%
umap_narrative/702_consensus_divisive_datamapplot.py 435 275 37%
umap_narrative/801_narrative_report_batch.py 666 455 32%
umap_narrative/803_check_batch_status.py 268 148 45%
umap_narrative/llm_factory_constructor/init.py 2 0 100%
umap_narrative/llm_factory_constructor/model_provider.py 234 107 54%
umap_narrative/narrative_data.py 178 65 63%
umap_narrative/numerical_stages.py 54 25 54%
umap_narrative/polismath_commentgraph/init.py 1 0 100%
umap_narrative/polismath_commentgraph/cli.py 268 268 0%
umap_narrative/polismath_commentgraph/core/init.py 3 3 0%
umap_narrative/polismath_commentgraph/core/clustering.py 108 108 0%
umap_narrative/polismath_commentgraph/core/embedding.py 104 104 0%
umap_narrative/polismath_commentgraph/lambda_handler.py 219 219 0%
umap_narrative/polismath_commentgraph/schemas/init.py 2 0 100%
umap_narrative/polismath_commentgraph/schemas/dynamo_models.py 160 9 94%
umap_narrative/polismath_commentgraph/tests/conftest.py 17 17 0%
umap_narrative/polismath_commentgraph/tests/test_clustering.py 74 74 0%
umap_narrative/polismath_commentgraph/tests/test_embedding.py 55 55 0%
umap_narrative/polismath_commentgraph/tests/test_storage.py 87 87 0%
umap_narrative/polismath_commentgraph/utils/init.py 9 7 22%
umap_narrative/polismath_commentgraph/utils/converter.py 283 237 16%
umap_narrative/polismath_commentgraph/utils/group_data.py 332 289 13%
umap_narrative/polismath_commentgraph/utils/storage.py 609 489 20%
umap_narrative/reset_conversation.py 162 53 67%
umap_narrative/run_pipeline.py 342 212 38%
umap_narrative/topic_naming.py 148 11 93%
Total 25760 8026 69%

@colinmegill
colinmegill merged commit 6a8b6b3 into stable Oct 17, 2026
59 of 63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant