Vote for your favorite SkillMD. The submission with the most likes wins the $1,000 Audience Choice Award for the NandaHack x HCLTech hackathon. Voting is open through October 25.Vote now →
mergedhumanPR #290Trust

[Fellowship Challenge] trust: reputation.capped.v1, a per-observer influence cap

trust/reputation.v1 sums +1/-1 per receipt with no bound on how much any single observer can move a score. reputation_consistent is explicit that it checks the recorded claim, not whether the reporter told the truth or held authority.

Author

adarsh-jha-dev avatar

@adarsh-jha-dev

github profile →
Status
Merged
Merged on
Oct 12
Branch
challenge/adarsh-jha-dev-capped-influence

Description

The pitch.

````md
# Submission for the Hexaware × Project NANDA Fellowship Technical Challenge

## The Gap

`trust/reputation.v1` sums `+1/-1` per receipt with no bound on how much any single observer can move a score. `reputation_consistent` is explicit that it checks the recorded claim, not whether the reporter told the truth or held authority.

One agent filing **N distinct bad receipts** against an honest seller drives that seller's score to `-N`, and every check still passes because each individual update follows the reference formula exactly. Volume from one voice, nothing forged.

This is not hypothetical. `registry_layer.py:51` sorts discovery by `-trust.score(name)`, and the reference buyer queries only the top two (`agents.py:105`). Five bad receipts from one agent that never trades, against three honest sellers:

```text
reputation.v1         s-honest -5   top2 ['s-rival-a','s-rival-b']   reached: NO
reputation.capped.v1  s-honest  0   top2 ['s-honest','s-rival-a']    reached: yes
````

The slandered seller is ranked last and never receives a quote request: an availability attack with no defence and no detection.

## This Is Not a Re-submission of Earlier Trust Work

No trust plugin other than `reputation.v1` exists on current `main` (`git ls-tree origin/main` shows only `layers/trust.py`).

The earlier submissions — EigenTrust (#2, #3, #6), TrustGuard (#27, #218), SybilResistantTrust (#139), WeightedTrust (#185), receipt-corroboration (#26), attested-peering (#149), and `bonded_trust` (#53) — live in `packages/nest-plugins-reference/` on `archive/legacy`, target `score_average`, and are `async`. None of that package tree or that reference plugin survives the rebuild.

The closest by mechanism is #53, `bonded_trust`, and it makes the opposite commitment on the same axis. Its docstring states that:

> a reporter holding a bond *majority* can dominate the sign of a score — that is the intended "most skin in the game, most say"

It bounds influence by **spent bond, independent of identity count**.

This plugin bounds influence **per observer, independent of standing**: no reporter can move a score by more than `1`, however much it has traded.

`bonded_trust` is a float bond-weighted mean over an injected `StakeLedger`; this is an integer sum of per-observer clamped contributions over an event the town already records.

One decides **who gets to dominate**; this one decides that **nobody does**.

They compose — an admission price and an influence bound are different questions — but they are not substitutes.

## The Layer

`trust`, and only `trust`.

New plugin: `reputation.capped.v1` (`src/nandatown/layers/trust_capped.py`), registered alongside `reputation.v1`, which is byte-identical to `origin/main`.

```text
score(subject) = sum over distinct observers of
                 weight(observer) * clamp(net(observer, subject), -1, +1)

net    = goods minus bads that observer reported about subject
weight = 1 if a settled payment names the observer, else 0
```

One observer can move a score by at most `1`, regardless of how many receipts it files.

An observer with no settled trade of its own moves nothing.

Distinct observers still accumulate, so the cap limits **a voice**, not the score.

A discarded report is still emitted with:

```json
{
  "delta": 0,
  "reason": "observer_cap_reached"
}
```

or:

```json
{
  "delta": 0,
  "reason": "observer_has_no_settled_trade"
}
```

The suppression is therefore present in the trace rather than absent from it.

## The Fault Is the Participant, Not a `FaultRule`

Neither scenario declares a `faults:` key, so the report prints:

```text
Faults: none
```

That is deliberate.

The fault is a new `slanderer` role (`src/nandatown/sim/agents.py`) that:

* registers,
* never trades, and
* files five attributed bad receipts about `seller-honest` at `0.5` logical-second intervals.

`FaultRule` offers only `dro

…

Try it

Open PR on GitHubView diff

Checkout locally

git fetch origin pull/290/head:pr-290
git checkout pr-290