Skip to content

test(wave-4): echo-chamber cohort #1 — consensus-with-dissent (W4.1, #196)#212

Open
Dewinator wants to merge 1 commit into
mainfrom
agent/wave-4-echo-chamber-fixture
Open

test(wave-4): echo-chamber cohort #1 — consensus-with-dissent (W4.1, #196)#212
Dewinator wants to merge 1 commit into
mainfrom
agent/wave-4-echo-chamber-fixture

Conversation

@Dewinator

Copy link
Copy Markdown
Owner

Summary

Fourth fixture row of the W4.1 anti-echo corpus (#196). Adds the echo-chamber cohort — the §10.4 diversity-filter trigger boundary case (over_concentration = 4/5 = 0.8), distinct from sybil-flood's saturation case (M=10, over_concentration=1.0).

The cohort is heterogeneous: 4 echoers + 1 visible dissenter. The load-bearing claim is that the dissenter's presence does NOT save the consensus lesson from the firebreak — §10.4 is purely a function of over_concentration, not of cohort heterogeneity. This is the case that defends against organic-looking groupthink where one might otherwise argue "but there's a dissenter, the cohort isn't really converged".

What's in the box

  • mcp-server/scripts/build-anti-echo-fixtures.mjs — Step 1d (write-once cohort-keys-echo-chamber.json, 5× Ed25519) + Step 6 (echo-chamber cohort fixture builder). Same write-once discipline as the plagiarism / sybil cohorts; distinct key file so a future refactor of either neighbouring cohort cannot silently shift the §10.4 boundary this row asserts on.
  • mcp-server/src/__tests__/fixtures/anti-echo/cohort-keys-echo-chamber.json — fixture-only Ed25519 keys for the 5 cohort peers.
  • mcp-server/src/__tests__/fixtures/anti-echo/echo-chamber/consensus-with-dissent-cohort.json — 5 signature-valid envelopes; envelopes[0..3] are echoers (shared content + evidence_root, near-duplicate embeddings via rampPerturbed(i+200), signed within a 3-minute window), envelopes[4] is the dissenter (heterodox content, distinct evidence_root, reversed-ramp embedding ≈ cosine 0.5 vs every echoer).
  • mcp-server/src/__tests__/anti-echo-echo-chamber.test.ts — 11 assertions:
    1. Metadata declares broadcast_suppressed, negative trust delta, §10.4 owner.
    2. cohort_size == envelopes.length and over_concentration ≥ 0.8 spec threshold.
    3. Every envelope is signature-valid against its cohort key.
    4. Distinct origin_node_id per envelope (5 peers, not one peer 5 times).
    5. Every envelope passes validateWireRecord (§10.4 is post-admission).
    6. Pairwise cosine > 0.95 within the echoer subset (§10.4 p_duplicate_cosine).
    7. Dissenter cosine < 0.95 vs every echoer (real heterodox peer, not a sock-puppet).
    8. signed_at spread ≤ §10.4 p_signed_at_window_d (7 days).
    9. Echoers share evidence_root; dissenter has its own.
    10. scoreFinding(prev=1.0, over=0.8) clamps new_weight=0.2 strictly below MIN_LOCAL_WEIGHT_FOR_BROADCAST (0.3) — even with a visible dissenter.
    11. Padding topic_cohort_size=100, near_dup_origin_count=80 (same 0.8 ratio) does NOT relax the multiplier — proves §10.4 has no count-dependent escape hatch.

Status of the W4.1 corpus after this PR

# Category Mechanism PR Status
1 forgery §3.7 + §5 r.1 #197 merged
2 plagiarism §10.4 #198 merged
3 sybil-flood §10.2 + §10.4 #201 merged
4 echo-chamber §10.4 this open
5 slow-poisoning §10.5 REM self-audit open
6 truth-by-repetition §10.4 + §10.6 open
7 polarity-inversion-pair §10.3 contradicts open
8 chain-rewrite §5 r.19 open

Test plan

  • node --test dist/__tests__/anti-echo-echo-chamber.test.js — 11/11 pass locally
  • node mcp-server/scripts/build-anti-echo-fixtures.mjs re-run is a no-op (write-once + identical fixture bytes)
  • npm run build clean (no new TS errors)

🤖 Generated with Claude Code

…196)

Fourth fixture row of the W4.1 anti-echo corpus. 5-peer cohort
(4 echoers + 1 dissenter) exercises the §10.4 diversity-filter
trigger boundary: over_concentration = 4/5 = 0.8 — the canonical
case rem-diversity.test.ts uses against the pure scoreFinding
helper, but here driven by signature-valid envelopes that
individually pass validateWireRecord.

Distinct from sybil-flood (the other §10.4-owning row):
sybil exercises the saturation path (M=10 sock-puppets,
over_concentration=1.0) — defends against coordinated rebroadcast.
Echo-chamber exercises the boundary path with a heterogeneous
cohort (4 echoers + 1 visible dissenter) — the load-bearing claim
is that the dissenter's presence does NOT save the consensus
lesson from the firebreak. §10.4 is purely a function of
over_concentration, not of cohort heterogeneity.

Eleven assertions cover: metadata invariants, signature validity
per cohort key, wire-validator admission of every envelope,
echoer-subset cosine > 0.95 (§10.4 p_duplicate_cosine), dissenter
cosine ≪ 0.95, signed_at spread ≤ 7 days, shared echoer
evidence_root vs distinct dissenter root, scoreFinding(prev=1.0,
over=0.8) clamps new_weight=0.2 below
MIN_LOCAL_WEIGHT_FOR_BROADCAST (0.3), and N-padding (cohort
N=100, M=80, ratio still 0.8) does not relax the multiplier —
proving §10.4 has no count-dependent escape hatch.

Build script extension (Step 1d + Step 6) follows the same
write-once cohort-keys discipline as plagiarism / sybil; distinct
key file so a future refactor of either neighbouring cohort
cannot silently shift the §10.4 boundary this row asserts on.

Tests: 11/11 pass under `node --test
dist/__tests__/anti-echo-echo-chamber.test.js`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dewinator added a commit that referenced this pull request May 4, 2026
…212

Doc was last refreshed 2026-05-03 when only row 3 (sybil-flood) was
shipped. Since then PRs #197 (forgery) and #198 (plagiarism) also
merged on 2026-05-03, and PR #212 (echo-chamber, row 4) is now in
flight behind the Wave-1 stack. The footer's "14-deep queue" reference
is also stale (queue is currently 5).

Changes:
- header: "row 3 shipped, 5 remaining" → "rows 1-3 merged, row 4 in PR
  queue, 4 remaining"
- TL;DR: enumerate PRs #197/#198/#201 as shipped and #212 as in-flight
- table: add rows 1 and 2 with their shipped-PR pointers; row 4 verdict
  is now "in PR #212 — test green, awaiting Reed merge"
- "What this means" section: drop the stale "queue's 14 PRs" wording
- fixture-key section: rows 5-8 (was 4-8) reuse the row-1 key; note
  plagiarism + echo-chamber both mint cohort keys following the same
  pattern as sybil-flood
- footer: "paused at 4/8 in flight" (was 3/8 against a 14-deep queue)

Direct-to-main, zero queue growth — saves the next W4.1 tick from
re-deriving shipped/in-flight state from gh + git log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dewinator added a commit that referenced this pull request May 4, 2026
…ture) + 26h Reed-lag

Last refresh (ec8a088, tick 173) captured the 4-PR native-app cluster (#208/#209/#210/#211)
right after Reed drained #202-#207. Tick 174 then opened PR #212 (W4.1 echo-chamber row 4),
which is off-track from the native-app cluster — it's a Wave-4 fixture PR against issue #196,
not a DbClient or sidecar-bundle PR. CLAUDE.md still framed the queue as 4 PRs all in one
narrative; this corrects it to 5 PRs with the fixture-PR called out separately, and surfaces
the empirical Reed-lag (≈26 h since the 2026-05-03 10:24 UTC merge wave) so the next tick has
the right baseline for the queue-too-deep guard.

Also corrects ~21 → 22 (true grep count today on main) for the Supabase-direct service file
count, with explicit "verbleibend nach Merge: 20" so the next DbClient-migration tick can
locate the right service to pick.

No production code touched; pure CLAUDE.md doc-accuracy fix, direct-to-main per the
agent-loop convention for risk-free doc refreshes.
@Dewinator

Copy link
Copy Markdown
Owner Author

Local validation (189th-tick fresh-clone walk, queue-bound, no-new-PR)

Walked PR #212 on a clean rebuild from agent/wave-4-echo-chamber-fixture (head da18c43). Echo-chamber fixture is self-contained — no sibling regression in the W4.1 anti-echo corpus.

#212 (this PR — W4.1 row 4 echo-chamber cohort, branch agent/wave-4-echo-chamber-fixture)

rm -rf dist && npm run build                                         → clean
node --test dist/__tests__/anti-echo-echo-chamber.test.js            → 11/11 pass (459ms)

Sibling-cohort regression (forgery / plagiarism / sybil-flood unchanged)

node --test dist/__tests__/anti-echo-forgery.test.js \
            dist/__tests__/anti-echo-plagiarism.test.js \
            dist/__tests__/anti-echo-sybil-flood.test.js             → 24/24 pass (474ms)

The write-once discipline holds: cohort-keys-echo-chamber.json is a fresh 5-key file (35 lines, distinct from cohort-keys-plagiarism.json and cohort-keys-sybil-flood.json). The cohort fixture itself is 3937 lines (5 signature-valid envelopes — 4 echoers + 1 dissenter). No file outside the four declared in the PR is touched.

What this PR uniquely asserts (re-read after running)

The two boundary-distinguishing assertions are the load-bearing ones:

  • Test 10scoreFinding at over_concentration=0.8, M=4 of 5 clamps below the broadcast threshold. This is the §10.4 trigger boundary (vs sybil-flood's over_concentration=1.0 saturation).
  • Test 11scoreFinding does NOT relax the firebreak when topic_cohort_size grows but over_concentration stays at 0.8. This is the dissenter-doesn't-save-it claim — §10.4 is purely a function of concentration, not heterogeneity. Without this, organic-looking groupthink with one visible dissenter would slip past the firebreak.

Together with row 1 (forgery, signature integrity), row 2 (plagiarism, citation echo), and row 3 (sybil-flood, saturation), row 4 closes the §10.4 boundary case. The remaining four W4.1 rows (per docs/wave-4-w4.1-row-triage.md) remain as separate fixtures.

#212 is independent of the DbClient stack (#209#210#211) and of #208 (sidecar-bundle). It can land in any order relative to those four; the file paths don't overlap.

No queue growth from this validation tick.

🤖 Generated with Claude Code

Dewinator added a commit that referenced this pull request May 4, 2026
The bird's-eye doc lagged the 2026-05-03 merge wave (PRs #202#207
landing sub-tasks 1, 2, 3-scaffold, 4, 5, 7-core) and the W3 spike
chain that settled the empirical mDNS budgets on `main`.

Changes:
- Wave 1 row: 9-PR queue → 5-PR queue (#208 sidecar bundling closing
  sub-task 3; #209#210#211 linear DbClient migration stack;
  unrelated W4.1 fixture #212). Note the 22-services DbClient sub-story
  with 2 already in flight.
- Wave 3 row: design-spike-only → 11-spike chain on `main` with named
  empirical budgets (64 KiB / 2 000 ms hostile-fetch, 5-s/3-fail
  heartbeat → 15 s eviction, ~1.3 s cold-rebrowse settle, 260 ms wake
  republish, 4 000 ms blocked-network threshold from K=10 trials).
  Cross-platform re-validation (Linux/Avahi, Windows) named as the
  open item before locking the library pick.
- Wave 1 narrative: matches the 5-PR reality, removes stale "9 PRs
  998/999 tests green" claim, keeps the queue-drain gate on sub-tasks
  6/8/9/10.

Verified: 11 spike files in experiments/swarm-discovery/, PRs
#202#207 all merged (gh pr list --state merged), 5 open PRs all
CLEAN+MERGEABLE. Refresh date bumped to 2026-05-04.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Dewinator

Copy link
Copy Markdown
Owner Author

Empirical merge-rebase validation (201st tick, 2026-05-04)

PR #212 validated locally end-to-end against current main (HEAD 180c1e9, 12 docs commits ahead of PR base ec8a088).

Method: fetched pull/212/head, git rebase origin/main (clean, no conflicts — confirms the GitHub mergeStateStatus = CLEAN), rm -rf dist && npm run build clean, then ran node --test against built artifacts.

Results on the rebased state:

  • Full mcp-server suite: 1017 / 1018 pass, 0 fail, 1 skip (7.8 s).
  • W4.1-specific (anti-echo-forgery.test.js + anti-echo-plagiarism.test.js + anti-echo-sybil-flood.test.js + anti-echo-echo-chamber.test.js): 35 / 35 pass (5 forgery, 9 plagiarism, 14 sybil-flood, 11 echo-chamber).
  • The PR body's load-bearing claim — "even with a visible dissenter, scoreFinding(prev=1.0, over=0.8) clamps new_weight=0.2 strictly below MIN_LOCAL_WEIGHT_FOR_BROADCAST (0.3)" — fires green in the rebased state. §10.4 has no count-dependent escape hatch in this code path either.
  • Build-script idempotency: node scripts/build-anti-echo-fixtures.mjs re-run on top of an already-populated tree → exits 0, git status empty (write-once + identical fixture bytes hold).

Implication for merge order: PR #212 is base = ec8a088 and the 12 commits since then are docs-only (wave-3, native-dbclient-tracker, wave-2, waves, claude.md, gitignore — zero overlap with mcp-server/src/__tests__/anti-echo-*.test.ts, mcp-server/src/__tests__/fixtures/anti-echo/**, or mcp-server/scripts/build-anti-echo-fixtures.mjs). The PR remains independent of #208 (sidecar bundling) and the #209/#210/#211 DbClient stack — Reed can land it in any order.

Tracker grounding (sanity-check ride-along): re-ran the docs/native-dbclient-migration-tracker.md .from(/.rpc( methodology against current main. Counts for all 22 listed services + the lesson-chain helper match the tracker's tabulated values exactly — Stand 2026-05-04 (195. tick recount) is still empirically grounded.

🤖 Posted by autonomous tick (mycelium 201st)

@Dewinator

Copy link
Copy Markdown
Owner Author

Local empirical validation 2026-05-05 (213th-tick) — 11/11 tests pass + spec/SQL cross-check

Fresh pr-212 checkout in mcp-server/, ran the full PR test file standalone:

npx tsx --test src/__tests__/anti-echo-echo-chamber.test.ts
ℹ tests 11
ℹ pass 11
ℹ fail 0

All 11 assertions green in 926ms — metadata, wire-signature (5×), wire-validator (5×), pairwise cosine (echoer subset > 0.95, dissenter ≪ 0.95), signed_at window, evidence_root structure, scoreFinding boundary, and the padded-cohort invariant.

Cross-check the load-bearing claim against the actual SQL gate

The PR claims over_concentration = 4/5 = 0.8 is the §10.4 trigger boundary. The spec text in docs/SWARM_SPEC.md:741 says >80% (strict greater-than), which would not fire at exactly 0.8. The PR's claim only holds if the SQL relaxes that to >=.

supabase/migrations/082_swarm_lesson_diversity.sql:199 confirms it does:

WHERE tc.topic_cohort_size >= p_min_cohort_size
  AND (nd.near_dup_origin_count::FLOAT / NULLIF(tc.topic_cohort_size, 0)) >= p_over_concentration;

And the migration's own header comment (lines 116–129) acknowledges the deliberate spec-text→implementation relaxation: "default 0.8 matches the issue's '5 peers, 4 hold the same' acceptance test, even though §10.4 spec text says [>80%]". Boundary-at-0.8 is intentional, not accidental — PR #212's fixture exercises exactly the case the migration was tuned for.

Why the padded-cohort test (M=80/N=100) is the strongest assertion

The second scoreFinding test is the one that closes a real attack vector: an adversary can't gather extra dissenters to dilute the concentration ratio downward, because §10.4's multiplier is (1 − over_concentration) — purely ratio-driven, with no N-dependent escape. Pinning that invariant in the row-4 fixture (rather than only at the M=4 boundary) is what makes this row a defense and not just a regression test.

Hands match feet across the §10 chain

  • scoreFinding(prev=1.0, over=0.8)new_weight = 0.2 (verified mathematically: factor = 1 - 0.8 = 0.2, candidate = 1.0 * 0.2 = 0.2, max(0, min(1.0, 0.2)) = 0.2).
  • MIN_LOCAL_WEIGHT_FOR_BROADCAST = 0.3 in swarm/endpoints/lesson-feed.js.
  • 0.2 < 0.3 → broadcast firebreak fires, lesson stays Tier-B. Matches the expected_outcome: "broadcast_suppressed" in the fixture metadata.

Mergeable from my side. No bugs surfaced. The boundary-vs-saturation split with sybil-flood (row 3) gives W4.1 two complementary §10.4 cases, and the row-1 rule-numbering correction (§5 rule 1§5 rule 18, commit d69570a on main) does not affect this row — owns_mechanism here is §10.4 only.

🤖 Generated with Claude Code

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