mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
refactor(cli): drop graperank --max-attempts, hardcode 3 retries
The per-user outbox retry bound doesn't need to be tunable — replace the --max-attempts flag with a MAX_OUTBOX_ATTEMPTS = 3 constant. Same behaviour, one fewer knob. Updates usage text, README, and the parity doc. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWk2ZMrGBSr4WenKgwqmbB
This commit is contained in:
@@ -89,7 +89,7 @@ It is **data**, not math:
|
||||
exactly the users our crawl discovers and whose kind 3/10000/1984 we fetch.
|
||||
So the effective scoring input is the same, as long as the crawl actually
|
||||
checks every discovered user's outbox — which it now does exhaustively (no
|
||||
user cap, retrying an unreachable outbox up to `--max-attempts` times).
|
||||
user cap, retrying an unreachable outbox a few times).
|
||||
2. **Fringe users / crawl gaps.** A relay timeout that drops a contact list
|
||||
removes edges and shifts nearby scores. The injector mitigates this with a
|
||||
two-stage model mirroring the app's `pickRelaysToLoadUsers`, plus a
|
||||
@@ -104,7 +104,7 @@ It is **data**, not math:
|
||||
when the outbox is unknown/down. **Indexers are not used for content** — they
|
||||
don't serve those kinds; kind:3/mutes/reports live only on the user's outbox.
|
||||
The crawl loops round by round, retrying any member whose contact list still
|
||||
didn't arrive (up to `--max-attempts`), until every discovered user's outbox
|
||||
didn't arrive (a few times), until every discovered user's outbox
|
||||
has been checked. Remaining mitigation lever: a generous `--timeout`.
|
||||
3. **Convergence precision.** Both stop at delta 0.0001; residual error is
|
||||
< ~0.0001 in influence ⇒ < ~0.01 rank points ⇒ identical integer `rank`.
|
||||
@@ -116,7 +116,7 @@ It is **data**, not math:
|
||||
- **Keep the current DEFAULT params** — they are byte-for-byte the Brainstorm
|
||||
DEFAULT preset. No change needed for parity.
|
||||
- **The crawl is exhaustive by default** (no user cap; every reachable user's
|
||||
outbox is checked, unreachable outboxes retried up to `--max-attempts`). An
|
||||
outbox is checked, unreachable outboxes retried a few times). An
|
||||
incomplete crawl is the single biggest source of drift, so avoid capping it.
|
||||
- **Optional, for fuller parity (not required for close scores):**
|
||||
- Add `--preset default|permissive|restrictive`. DEFAULT is confirmed; the
|
||||
|
||||
@@ -529,12 +529,11 @@ private fun printUsage() {
|
||||
|Web of Trust (GrapeRank):
|
||||
| graperank [OBSERVER] compute subjective trust scores (0..1) for every
|
||||
| [--limit N] [--min-score X] user reachable in the follow/mute/report graph.
|
||||
| [--rigor X] [--attenuation X] Crawls each user's kind:10002 outbox for their
|
||||
| [--max-attempts N] [--max-rounds N] latest kind:3/10000/1984 until every discovered
|
||||
| [--offline] [--timeout SECS] user has been checked (no user cap; --max-attempts
|
||||
| [--publish] [--min-rank N] bounds retries of an unreachable outbox, default 3).
|
||||
| [--publish-limit N] [--publish-relay URL] OBSERVER: npub|nprofile|hex|name@domain (default:
|
||||
| active account). --offline scores from the local
|
||||
| [--rigor X] [--attenuation X] Exhaustively crawls each user's kind:10002 outbox
|
||||
| [--max-rounds N] for their latest kind:3/10000/1984 until every
|
||||
| [--offline] [--timeout SECS] discovered user has been checked (no user cap).
|
||||
| [--publish] [--min-rank N] OBSERVER: npub|nprofile|hex|name@domain (default:
|
||||
| [--publish-limit N] [--publish-relay URL] active account). --offline scores from the local
|
||||
| store only. --publish writes NIP-85 kind:30382
|
||||
| cards (rank = round(score*100)) for each user at
|
||||
| or above --min-rank (unchanged ranks skipped).
|
||||
|
||||
@@ -60,8 +60,8 @@ import kotlin.math.roundToInt
|
||||
* their kind:3 / kind:10000 / kind:1984 events are fetched from *their own*
|
||||
* relays. The crawl is exhaustive: it keeps going, with no user cap, until every
|
||||
* discovered user's outbox has been checked and their contact list pulled (an
|
||||
* unreachable outbox is retried up to `--max-attempts` times), then runs the
|
||||
* scoring engine in `commons/wot`.
|
||||
* unreachable outbox is retried a few times), then runs the scoring engine in
|
||||
* `commons/wot`.
|
||||
*
|
||||
* Prints a ranked list (text, or one JSON object under `--json`). With
|
||||
* `--publish`, results are also published as NIP-85 kind:30382 `ContactCardEvent`
|
||||
@@ -84,6 +84,10 @@ object GrapeRankCommand {
|
||||
// Emit a scoring-progress line every this many worklist visits.
|
||||
private const val SCORE_PROGRESS_STEP = 5_000
|
||||
|
||||
// Times we re-query an unreachable user's outbox before giving up on it, so
|
||||
// the crawl still terminates on a finite graph.
|
||||
private const val MAX_OUTBOX_ATTEMPTS = 3
|
||||
|
||||
suspend fun dispatch(
|
||||
dataDir: DataDir,
|
||||
tail: Array<String>,
|
||||
@@ -103,10 +107,8 @@ object GrapeRankCommand {
|
||||
val args = Args(rest)
|
||||
val observerArg = args.positionalOrNull(0)
|
||||
// Crawl to full convergence by default (every reachable user's outbox
|
||||
// checked). --max-rounds is only a safety backstop; --max-attempts bounds
|
||||
// how many times we re-try an unreachable user's outbox before giving up.
|
||||
// checked). --max-rounds is only a safety backstop.
|
||||
val maxRounds = args.intFlag("max-rounds", Int.MAX_VALUE)
|
||||
val maxAttempts = args.intFlag("max-attempts", 3)
|
||||
val limit = args.intFlag("limit", 100)
|
||||
val minScore = args.flag("min-score")?.toDoubleOrNull() ?: 0.0
|
||||
val offline = args.bool("offline")
|
||||
@@ -153,8 +155,8 @@ object GrapeRankCommand {
|
||||
|
||||
// Loop until every discovered user has had their outbox checked and
|
||||
// their kind:3/10000/1984 pulled from it — no user cap. A user whose
|
||||
// outbox stays unreachable is dropped after --max-attempts tries so
|
||||
// the crawl still terminates.
|
||||
// outbox stays unreachable is dropped after MAX_OUTBOX_ATTEMPTS tries
|
||||
// so the crawl still terminates.
|
||||
while (rounds < maxRounds) {
|
||||
val pending = discovered.filterNot { it in done }
|
||||
if (pending.isEmpty()) break
|
||||
@@ -187,7 +189,7 @@ object GrapeRankCommand {
|
||||
attempts[pk] = tries
|
||||
// Give up once we've exhausted retries: either the user has
|
||||
// no contact list, or their outbox is unreachable.
|
||||
if (tries >= maxAttempts) done += pk
|
||||
if (tries >= MAX_OUTBOX_ATTEMPTS) done += pk
|
||||
}
|
||||
}
|
||||
System.err.println(
|
||||
|
||||
Reference in New Issue
Block a user