Skip to content

Correct the aggregation algorithm: issuer capping now binds (v1.1) - #58

Merged
blocksifrdev merged 1 commit into
mainfrom
fix/aggregation-v1.1
Sep 15, 2026
Merged

blocksifrdev merged 1 commit into
mainfrom
fix/aggregation-v1.1

Conversation

@blocksifrdev

Copy link
Copy Markdown
Collaborator

Normative change. This alters conformance for anyone implementing protocol/aggregation-spec.md.

Implementing the spec surfaced a defect in the spec.

What was wrong

Step 5 capped a dominant issuer at max_issuer_weight (0.40) and then re-normalized across all issuers — which handed the capped excess straight back whenever the other issuers were light.

With 50 receipts from one issuer and one each from two others:

v1.0 v1.1
Weight held by the "capped" issuer 87% 40%
Aggregate score 0.90 0.52

The cap only bound when the field was already balanced — precisely the case where a cap isn't needed. An issuer that is chatty, compromised, or simply wired to emit more receipts could still decide an agent's trust alone, which is the one thing step 5 exists to prevent.

The fix

Redistribute a capped issuer's excess to the uncapped issuers, and apply:

effective_cap = max(max_issuer_weight, 1 / issuer_count)

since a cap below 1/n cannot be satisfied — with two issuers, both cannot sit under 0.40.

The vectors already knew

agg-006 was written to assert exactly this intent"4 good receipts from A cannot dominate 1 bad receipt from B" — and could not pass under v1.0. It passes under v1.1 unchanged. That's the clearest evidence available that the vector recorded the intent and the algorithm had drifted from it.

Two other vectors were repaired:

  • agg-003 contradicted itself: its _explanation field worked through the arithmetic, caught itself mid-sentence ("wait let me recalculate"), and concluded 0.5 — while expected still said 0.4. It was already superseded by agg-003-corrected, which now carries the agg-003 id.
  • agg-008 recomputed from unrounded intermediate weights: 0.917 → 0.918.

Three added: agg-009 (redistribution), agg-010 (the 1/n floor), agg-011 (single issuer, nobody to redistribute to).

All eleven now pass on their own terms, and the known-divergence scaffolding I added in #57 is gone.

Migration

A v1.0 implementation produces different scores wherever one issuer exceeds the cap while the others are light. Step 5 carries the warning inline and CHANGELOG.md repeats it. I did not touch max_issuer_weight, the decay half-life, or negative-signal amplification — only how the cap is applied.

Worth flagging: this sits close to the issuer-weight-normalization method docs/patent-strategy.md names as patent-pending, so the published algorithm matching its published intent likely matters beyond engineering.

194 tests, Python 19, AGT parity green.

🤖 Generated with Claude Code

Implementing the normative spec surfaced a defect in it. Step 5 capped a
dominant issuer at max_issuer_weight and then re-normalized across all
issuers, which handed the capped excess straight back whenever the other
issuers were light. With 50 receipts from one issuer and one each from
two others, the "capped" issuer still held 87% of the weight and the
aggregate came out at 0.90. The cap only bound when the field was already
balanced — the case where a cap is not needed.

v1.1 redistributes a capped issuer's excess to the uncapped issuers, and
applies effective_cap = max(max_issuer_weight, 1 / issuer_count), since a
cap below 1/n cannot be satisfied. That input now gives the dominant
issuer exactly 0.40 and an aggregate of 0.52.

Vector agg-006 was written to assert exactly this intent — "4 good
receipts from A cannot dominate 1 bad receipt from B" — and could not
pass under v1.0. It passes under v1.1 unchanged, which is the clearest
evidence that the vector recorded the intent and the algorithm had drifted
from it.

Vectors repaired and extended:
- agg-003 contradicted itself: its _explanation worked the arithmetic and
  concluded 0.5 while expected said 0.4. It was already superseded by
  agg-003-corrected, which now carries the agg-003 id.
- agg-008 recomputed from unrounded weights, 0.917 -> 0.918.
- Added agg-009 (redistribution), agg-010 (the 1/n floor) and agg-011
  (single issuer, nobody to redistribute to).

All eleven vectors now pass on their own terms; the known-divergence
scaffolding in the test suite is gone.

This changes conformance: a v1.0 implementation produces different scores
wherever one issuer exceeds the cap while the others are light. The spec
carries that warning in step 5 and the changelog repeats it.

194 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@blocksifrdev
blocksifrdev merged commit 72c301b into main Sep 15, 2026
4 checks passed
@blocksifrdev
blocksifrdev deleted the fix/aggregation-v1.1 branch September 15, 2026 16:15
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