Skip to content

feat(trust-score): F2 cold-start score from public data for new agents#73

Merged
MoltyCel merged 1 commit into
mainfrom
feat/cold-start-score
May 25, 2026
Merged

feat(trust-score): F2 cold-start score from public data for new agents#73
MoltyCel merged 1 commit into
mainfrom
feat/cold-start-score

Conversation

@MoltyCel
Copy link
Copy Markdown
Owner

Summary

  • Adds a cold-start trust score derived from public signals (Basescan, GitHub, ERC-8004) so brand-new agents look different from inactive ones in /skill/trust-score/{did}.
  • Activates only when endorser_count == 0 — Phase 2 takes over the moment endorsements arrive.
  • Returns honest null with basis "no_public_data" when no source has data. No fabricated defaults.

Scoring (max per source)

Source Max Components
On-chain wallet (Base L2 via Basescan) 20 tx_count/10 ≤ 8, age_days/30 ≤ 7, usdc_volume/1000 ≤ 5
GitHub account 15 repos/3 ≤ 5, account_age/60 ≤ 5, recent_commits/10 ≤ 5
ERC-8004 registry presence 10 +10 if wallet in erc8004_outreach
Hard ceiling 60 A cold-start agent cannot outrank an endorsed one

Confidence: none (no sources) → medium (1 source) → high (≥2 sources).

Response shape (new fields, when endorser_count == 0)

{
  "did": "did:moltrust:abc...",
  "trust_score": null,                 // (Phase 2 still withholds)
  "endorser_count": 0,
  "cold_start_score": 34.2,
  "cold_start_basis": "onchain+erc8004",
  "cold_start_confidence": "high",
  "cold_start_computed_at": "2026-05-25T...",
  "cold_start_note": "Score based on public data. Will be replaced by behavioral history."
}

Touched files

File Change
app/migrations/007_cold_start_score.sql 4 new columns on agents (score, basis, confidence, computed_at)
app/cold_start.py New — fetchers (Basescan, GitHub), pure scorer, 24h-cached orchestrator
app/main.py Non-breaking addition in /skill/trust-score/{did} — only fires when endorser_count == 0
tests/test_cold_start.py 10 pure-unit tests for the scoring formula (no IO)

Out of scope (intentional)

  • github_url is not yet a column on agents; the GitHub fetcher is wired but fed None for now. Will activate once registration captures the field.
  • HTTP retry / circuit-breaker for Basescan — failures degrade silently to None, which is the documented behaviour.

Migration plan

  1. Merge this PR.
  2. Run app/migrations/007_cold_start_score.sql on the production DB (idempotent — uses ADD COLUMN IF NOT EXISTS).
  3. Restart moltrust-api (config reload picks up the new module).
  4. Verify: hit /skill/trust-score/{a-new-did} — expect cold_start_* fields populated within ~8s (Basescan timeout).

Test plan

  • pytest tests/test_cold_start.py — 10/10 pass (server scratch w/ venv)
  • Import-smoke (CI env): from app.main import app → OK
  • python -m compileall on app/cold_start.py + app/main.py → clean
  • Post-deploy: smoke an actual fresh DID against staging, confirm Basescan path actually populates cold_start_score when wallet has on-chain history
  • Post-deploy: smoke a DID with no wallet → expect cold_start_score: null, basis: "no_public_data"
  • Post-deploy: confirm 24h cache holds — second call within TTL must not re-hit Basescan

🤖 Generated with Claude Code

Phase 2 trust scoring withholds a score for agents with fewer than three
endorsements, leaving brand-new agents indistinguishable from inactive
ones. This change derives a `cold_start_score` from public signals so
the onboarding response carries something honest instead of an
ambiguous null.

Sources (max contribution each):
- On-chain wallet history via Basescan (Base L2) ........ 20 pts
  tx_count / 10 (cap 8), age_days / 30 (cap 7), usdc_volume / 1000 (cap 5)
- GitHub account activity (when github_url available) ... 15 pts
- ERC-8004 registry presence (local outreach scan) ...... 10 pts
Hard ceiling: 60.0 — a cold-start agent can never appear stronger than
an endorsed one. Score is `null` with basis `no_public_data` when no
source returns data (deliberate — we do not fabricate a default).

Integration: `GET /skill/trust-score/{did}` returns the new
`cold_start_*` fields ONLY when `endorser_count == 0`. As soon as
real endorsements arrive the Phase 2 score takes over.

Cache: 24h TTL, stored on `agents.cold_start_*` columns to avoid
hammering Basescan/GitHub on every lookup.

Changes:
- app/migrations/007_cold_start_score.sql — 4 columns on `agents`
- app/cold_start.py — fetchers + pure scoring + 24h-cached orchestrator
- app/main.py — non-breaking addition to `/skill/trust-score/{did}`
- tests/test_cold_start.py — 10 pure-unit tests for the scoring formula

Out of scope (documented as future work):
- `github_url` is not yet a column on `agents`; the GitHub fetcher is
  wired but currently fed `None`. Activates once registration captures
  the field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MoltyCel MoltyCel merged commit 915598e into main May 25, 2026
12 checks passed
@MoltyCel MoltyCel deleted the feat/cold-start-score branch May 25, 2026 10:54
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