La pagina di un repository su GitHub espone pushed_at, un timestamp che può aggiornarsi quando viene fatto un push su qualunque branch. Quindi non coincide per forza con la data dell’ultimo commit sul branch predefinito. La data del branch predefinito è un segnale dello stato di manutenzione del repository; non corrisponde necessariamente al codice che un package manager installa.
Di solito, i package manager risolvono artefatti del registry o versioni dei moduli. Per questo questa analisi verifica separatamente l’attività del repository e l’artefatto pubblicato: uno dei due può essere aggiornato mentre l’altro è già vecchio.
Ho quindi preso 35 repository che compaiono ancora nei suggerimenti e ho letto il dato che GitHub non mostra nell’header: la data del commit più recente sul branch predefinito.
La discrepanza esiste davvero: in 14 dei 35 casi pushed_at risulta oltre 180 giorni più avanti rispetto all’ultimo commit sul branch predefinito, con un massimo di 1.802 giorni. In tre di questi 14 casi la presenza di bot è stata confermata con certezza; dopo aver escluso repository archiviati e casi con attività umana, restano due casi confermati di bot su nove candidati. Il punto davvero utile è che la freschezza del repository e quella dell’artefatto pubblicato possono divergere.
Cosa è stato misurato, e su cosa

Riferimento ufficiale: GitHub repository API.
Tutti i numeri riportati qui sono stati letti da risposte API live tra le 15:44 e le 15:53 UTC del 2026-07-27 e poi messi in cache. Il dataset di 35 righe, la lista dei repository, le righe generate e gli script di fetch/build in artifacts/ conservano input e trasformazioni dell’audit.
Sono state raccolte quattro categorie di dati; le richieste al registry e all’attività sono state condizionali, non una sequenza fissa di quattro chiamate:
GET /repos/{owner}/{repo}— stelle,archived,pushed_at, licenza,default_branch.GET /repos/{o}/{r}/commits?sha={default_branch}&per_page=1— l’ultimo commit sul branch predefinito, usato come segnale di manutenzione del repository.GET /repos/{o}/{r}/activity?per_page=30— per ogni repository in cui i due dati non coincidono, cosa ha davvero aggiornatopushed_at.GET /repos/{o}/{r}/releasespiù i registry PyPI e npm — quando l’artefatto è stato pubblicato l’ultima volta, che alla fine si rivela più importante di entrambi.
staleness_days è il tempo trascorso dal commit sul branch predefinito al momento di riferimento. Il divario tra pushed_at e quel commit è l’illusione, misurata in giorni. Qualsiasi valore oltre 180 giorni viene segnalato.
Vale la pena chiarire due aspetti perché hanno cambiato i risultati.
L’attribuzione dei package è stata verificata, mai dedotta. Un package il cui README cita un repository non è per forza il package di quel repository. Ogni corrispondenza ha dovuto essere confermata tramite un campo strutturato — la voce repository/project_urls del registry, oppure un manifest committato dentro il repository. Sei corrispondenze apparentemente plausibili non hanno superato questo controllo e i relativi download non vengono attribuiti apposta.
Una di queste esclusioni vale da sola la regola. curl-cffi registra 35.763.529 download al mese e, da lontano, sembra il binding Python di lwthiker/curl-impersonate — un repository fermo da 875 giorni. Attribuirlo avrebbe prodotto un numero quarantaquattro volte superiore a quello di newspaper3k, ma sarebbe stato falso: i metadati PyPI di curl-cffi puntano a lexiforest/curl_cffi, un progetto separato e ancora mantenuto, pubblicato l’ultima volta il 2026-04-03. Il numero più clamoroso disponibile qui era quello sbagliato.
Un settimo caso è ancora più insolito: steel-dev/steel-mcp-server dichiara @steel-dev/mcp-server nel proprio package.json, ma npm restituisce 404. Non è mai stato pubblicato, quindi non si può dire che sia “ancora installato”.
Quando un dato non si poteva ottenere, è stato segnalato chiaramente. Gli strumenti Go, JVM, .NET e PHP non hanno presenza su PyPI o npm, quindi vengono indicati come N/A (nessun package PyPI/npm) — mai come zero. Diciassette dei 35 non pubblicano alcun GitHub Release; questo è registrato come none, non come dato mancante.
Questi campi non sono stati compressi apposta in un unico “punteggio di salute”. Un branch predefinito vecchio, un branch laterale recente, un GitHub Release assente e un artefatto di registry vecchio rispondono a domande diverse. Le evidenze a livello di riga sono disponibili nel dataset di 35 righe, insieme agli input dei repository e ai record generati. Vanno letti come segnali di triage che guidano il controllo successivo, non come quattro voti sulla vitalità di un progetto.
La cautela sul campione, detta subito
Questa è una lista costruita a mano di strumenti che sospettavo stessero tirando a campare. Non è un campione casuale dell’ecosistema dello scraping, e “31 su 35 sono vecchi” non è un tasso dell’ecosistema — è quasi una misura di quanto bene ho scelto io il campione. Il risultato interessante non è il conteggio dei repo stantii. È che, anche su un campione scelto apposta per mostrare quel fenomeno, il meccanismo specifico che stavo testando spiegava solo una minoranza dei casi, e poteva essere confermato con certezza in ancora meno casi.
L’illusione è reale, e questo è il caso peggiore
sjdirect/abot, un crawler .NET con 2.308 stelle. GitHub indica un push del 2026-07-17, dieci giorni prima della data di riferimento. Il branch predefinito è stato toccato l’ultima volta il 2021-08-09.
Sono 1.802 giorni di distanza. Cinque anni. L’header dice “la settimana scorsa”.
Quattordici dei 35 repository mostrano un divario superiore a 180 giorni:
| Repo | Divario (giorni) | Ultimo movimento sul branch predefinito | pushed_at |
|---|---|---|---|
sjdirect/abot | 1.802 | 2021-08-09 | 2026-07-17 |
dragnet-org/dragnet | 1.520 | 2021-05-09 | 2025-07-08 |
paquettg/php-html-parser | 1.376 | 2020-11-01 | 2024-08-09 |
seomoz/simhash-py | 1.159 | 2020-03-12 | 2023-05-15 |
internetarchive/wayback | 1.039 | 2021-04-27 | 2024-03-01 |
Rhizome-Conifer/conifer | 1.013 | 2023-10-12 | 2026-07-22 |
kohlschutter/boilerpipe | 856 | 2015-08-30 | 2018-01-03 |
scrapinghub/splash | 819 | 2022-05-05 | 2024-08-02 |
tomnomnom/waybackurls | 756 | 2022-04-05 | 2024-05-01 |
geziyor/geziyor | 689 | 2024-08-12 | 2026-07-02 |
crawlab-team/crawlab | 488 | 2024-10-09 | 2026-02-10 |
ArchiveTeam/wpull | 468 | 2023-01-16 | 2024-04-29 |
yasserg/crawler4j | 396 | 2020-10-03 | 2021-11-04 |
apache/any23 | 381 | 2022-06-03 | 2023-06-20 |
Quattordici su trentacinque. È un dato reale, utile da conoscere, e una minoranza di un campione scelto per contenerlo.
Su tre repository segnalati è confermata l’attività dei bot — e due restano dopo il filtro
La versione popolare di questa storia fa sempre il nome di dependabot. Ho controllato, estraendo il feed di attività per ogni repository segnalato e classificando ogni ref pushato dopo l’ultimo commit del branch predefinito. Quel “dopo” è fondamentale: gli eventi anteriori all’ultimo commit non spiegano nulla del divario, e considerare tutto il feed cambia silenziosamente la domanda.
Confermati come bot-driven, con il metodo post-commit: tre. scrapinghub/splash (4 eventi su 4 post-commit su dependabot/pip/*), geziyor/geziyor (5 su 5 su dependabot/go_modules/*), apache/any23 (16 su 16 su dependabot/maven/*). Uno di questi tre, any23, è formalmente archiviato, quindi non entra mai nel set filtrato — lasciando due casi confermati che soddisfano anche tutte le altre condizioni.
Sbagliato in modo netto: due, e sono entrambi più interessanti della storia dei bot.
Il divario di 1.520 giorni di dragnet-org/dragnet deriva da un umano che ha pushato un branch chiamato mp/py3.10 — un port Python 3.10 mai mergiato. Qualcuno ha provato a portarlo avanti e poi si è fermato. Non è rumore automatico che gonfia un timestamp; è una traccia visibile e datata di un tentativo di salvataggio fallito. A ben vedere, è il segnale più utile dell’intero dataset, e la narrazione “è stato dependabot” lo cancellerebbe.
Misto, e più grande di entrambi: due. crawlab-team/crawlab ha 12.250 stelle — il secondo repository più popolare del campione — e un divario di 488 giorni su main. Nel feed compaiono branch di dependabot e 24 push post-commit da parte di umani, tutti su develop e test. Chi legge l’header vede febbraio 2026 e pensa che il progetto sia vivo; chi guarda main vede ottobre 2024 e pensa che sia morto. Entrambe le conclusioni sono sbagliate. Lo sviluppo si è spostato fuori dal branch predefinito, cosa che i progetti fanno spesso e che la vista sintetica di GitHub non riesce a rappresentare. sjdirect/abot è l’altro caso misto: il push che ha fissato il suo pushed_at principale era davvero di dependabot, ma nel 2024 un umano ha pushato upgrade1, ed è per questo che più avanti esce dal set filtrato.
Rhizome-Conifer/conifer è il caso ambiguo, e inizialmente l’ho letto male. Il branch predefinito è main, non master, e main è fermo dal 2023-10-12 — l’unico evento main nel feed è la creazione del branch nel gennaio 2025, coerente con una rinomina. Nel frattempo un singolo account ha pushato su conifer-twilight e twilight/read-only il 2026-07-22, cinque giorni prima della data di riferimento. È attività umana reale, ma “sviluppato attivamente” è una conclusione più forte di quella consentita dai ref: un solo contributor, su branch chiamati read-only, è almeno compatibile con una chiusura gestita quanto con uno sviluppo in corso. Quello che si può dire è più ristretto ma vale comunque: il divario di 1.013 giorni non è rumore generato da bot, e non è nemmeno prova di abbandono.
Sconosciuto: sette. php-html-parser, simhash-py, internetarchive/wayback, boilerpipe, waybackurls, wpull e crawler4j hanno tutti un divario verificato e un feed di attività che restituisce vuoto.
La spiegazione più intuitiva è la retention — il feed di attività di GitHub non arriva all’infinito. La cache smentisce questa ipotesi per la maggior parte di essi. L’evento più vecchio in tutte queste 123 risposte è il 2023-03-10, e cinque dei sette hanno un pushed_at comodamente dentro quella finestra: php-html-parser 2024-08-09, wpull 2024-04-29, waybackurls 2024-05-01, internetarchive/wayback 2024-03-01, simhash-py 2023-05-15. Qualunque cosa abbia aggiornato quei timestamp avrebbe dovuto comparire nel feed, e invece non c’è. La retention spiega solo boilerpipe (2018) e crawler4j (2021).
Quindi la formulazione onesta è più debole di una spiegazione elegante: per sette repository il divario è un fatto verificato e la sua causa non è stabilita — l’endpoint non ha restituito nulla, e per cinque di questi non posso dire perché. Dire “è stato dependabot” è un’ipotesi per tutti e sette.
Nel complesso, sui 14 repository segnalati:
Causa del pushed_at gonfiato | Repo | Quali, e su quale evidenza |
|---|---|---|
| Confermato come bot-driven | 3 | scrapinghub/splash (4 eventi su 4 post-commit su dependabot/pip/*), geziyor/geziyor (5 su 5 su dependabot/go_modules/*), apache/any23 (16 su 16 su dependabot/maven/*) — any23 è archiviato, lasciando due casi che soddisfano anche tutte le altre condizioni |
| Misto, bot e umano | 2 | crawlab-team/crawlab (branch di dependabot più 24 push post-commit da parte di umani, tutti su develop e test), sjdirect/abot (il push che ha fissato il suo pushed_at principale era davvero di dependabot, ma un umano ha pushato upgrade1 nel 2024) |
| Sbagliato in modo netto — lavoro umano, zero branch bot | 2 | dragnet-org/dragnet (3 eventi post-commit, 0 su un branch bot) — un umano ha pushato mp/py3.10, un port Python 3.10 non mergiato. Rhizome-Conifer/conifer (30 eventi post-commit, 0 su un branch bot) — un singolo account ha pushato conifer-twilight e twilight/read-only il 2026-07-22. Se conifer sia abbandonato resta ambiguo, come detto sopra; ciò che non è ambiguo è che nessun bot ha gonfiato il suo pushed_at |
| Non stabilito — feed di attività vuoto | 7 | php-html-parser, simhash-py, internetarchive/wayback, boilerpipe, waybackurls, wpull, crawler4j |
Le righe sommano a 14. Classificano cosa ha mosso pushed_at; non stabiliscono in modo indipendente se un progetto sia abbandonato.
Cosa sopravvive al filtro completo, e cosa significa “sopravvive”
La tesi originale richiede quattro condizioni insieme: stantio da oltre un anno, pushed_at gonfiato oltre 180 giorni, non archiviato e nessun segnale che il gonfiamento derivi da lavoro umano. Nove su 35 candidati superano tutte e quattro le condizioni, elencati qui accanto ai due casi di esclusione più importanti:
| Repo | Supera tutte e quattro? | Evidenza positiva che i bot hanno causato il gonfiamento |
|---|---|---|
splash | sì | sì — 4 eventi su 4 post-commit su dependabot/pip/* |
waybackurls | sì | nessuna direzione di evidenza |
crawler4j | sì | nessuna direzione di evidenza |
geziyor | sì | sì — 5 su 5 su dependabot/go_modules/* |
php-html-parser | sì | nessuna direzione di evidenza |
boilerpipe | sì | nessuna direzione di evidenza |
wpull | sì | nessuna direzione di evidenza |
internetarchive/wayback | sì | nessuna direzione di evidenza |
simhash-py | sì | nessuna direzione di evidenza |
any23 | no — archiviato | sì — 16 su 16 su dependabot/maven/*, la conferma più forte dell’intero audit |
abot | no — il suo record contiene un push umano (upgrade1, 2024) | misto — il push che ha fissato il suo pushed_at principale era davvero di dependabot |
Quel numero richiede una precisazione che il filtro non può contenere. Solo due dei nove — splash e geziyor — hanno evidenza positiva che i bot abbiano causato il gonfiamento. Gli altri sette superano la soglia solo perché non c’è evidenza in un senso o nell’altro. Sono casi che la tesi non smentisce, non casi che la confermano. E la conferma più forte dell’intero audit, any23 con 16 eventi su 16 di dependabot, è esclusa perché il repository è archiviato.
Notare anche che abot, il caso da 1.802 giorni, non è tra i nove. Il suo record contiene un push umano, quindi fallisce la quarta condizione — l’illusione più evidente del dataset non è un esempio pulito del meccanismo che illustra.
Per 21 repository su 35, GitHub mostrava chiaramente la vecchia data
Ecco il risultato che ha fatto più danni alla mia ipotesi. Quattordici repository hanno un divario pari a zero, e altri sette restano sotto i 180 giorni. Per 21 candidati su 35, pushed_at coincide con l’ultimo commit sul branch predefinito. GitHub non sta nascondendo nulla.
Inclusi alcuni degli oggetti più vecchi del campione:
| Repo | Stelle | Stantio (giorni) | Divario |
|---|---|---|---|
Janpot/microdata-node | 57 | 1.866 | 0 |
1e0ng/simhash | 1.037 | 1.606 | 21 |
ekzhu/SetSimilaritySearch | 603 | 1.384 | 0 |
GerbenJavado/LinkFinder | 4.431 | 834 | 0 |
hakluke/hakrawler | 5.099 | 582 | 0 |
lavague-ai/LaVague | 6.388 | 551 | 0 |
my8100/scrapydweb | 3.411 | 522 | 0 |
getomni-ai/zerox | 12.258 | 432 | 0 |
scrapinghub/frontera | 1.332 | 415 | 0 |
BuilderIO/gpt-crawler | 22.374 | 384 | 0 |
BuilderIO/gpt-crawler ha 22.374 stelle e il suo header dice la stessa cosa dal 2025-07-07. Non c’è nulla di nascosto, e il volume di installazioni continua.
Questo costringe la tesi a diventare più stretta: GitHub nasconde la vecchiaia solo in una minoranza di casi, mentre nella maggioranza la mostra chiaramente anche se le installazioni continuano comunque. Perché continuino è fuori dalla portata di questi dati — qui i numeri contano le installazioni, non le decisioni. Ma nessuna modifica dell’interfaccia risolve il secondo gruppo, che è quello più numeroso.
L’attività del repository e gli artefatti pubblicati possono divergere
La singola riga più sorprendente del dataset rompe del tutto l’impostazione.
codelucas/newspaper — 15.126 stelle — è attivo. Il suo ultimo commit sul branch predefinito è datato 2026-07-21, rispetto alla data di riferimento 2026-07-27, ed è stato scritto dal manutentore. Tutti i controlli a livello di repository sono superati.
Il package che tutti installano è newspaper3k 0.2.8, pubblicato il 2018-09-28. Ha 2.858 giorni di età e registra 813.513 download al mese.
Il branch predefinito è aggiornato, mentre l’artefatto PyPI non viene pubblicato dal 2018. Questo prova l’esistenza di un ritardo nelle release, non il motivo per cui il package non sia stato pubblicato né se la pipeline sia rotta. È il tipo di rischio che un controllo solo sul repository non vedrebbe, perché l’artefatto del registry è ciò che normalmente viene installato dopo pip install newspaper3k.
Guardando i package invece dei repository, il pattern è ovunque. Tra i 17 package la cui attribuzione al repository è stata verificata, 16 sono stati pubblicati l’ultima volta più di un anno fa, e quei 16 rappresentano circa 2,28 milioni di installazioni al mese su 2,30 milioni totali:
| Package | Installazioni/mese | Ultima pubblicazione | Età del package (giorni) |
|---|---|---|---|
newspaper3k | 813.513 | 2018-09-28 | 2.858 |
tls-client | 790.305 | 2024-02-02 | 905 |
simhash | 317.615 | 2022-03-03 | 1.606 |
microdata-node | 204.025 | 2020-05-11 | 2.267 |
@modelcontextprotocol/server-puppeteer | 127.232 | 2025-05-12 | 440 |
extract-thinker | 10.927 | 2025-06-09 | 412 |
SetSimilaritySearch | 7.984 | 2022-10-11 | 1.384 |
frontera | 4.709 | 2019-04-05 | 2.669 |
zerox | 3.303 | 2025-05-20 | 432 |
scrapydweb | 1.163 | 2025-02-16 | 525 |
lavague | 606 | 2024-08-05 | 720 |
splash | 333 | 2020-06-16 | 2.231 |
dragnet | 213 | 2019-04-16 | 2.658 |
@builder.io/gpt-crawler | 137 | 2025-01-23 | 549 |
lmnr-index | 120 | 2025-06-05 | 416 |
simhash-py | 113 | 2017-03-22 | 3.413 |
tls-client merita una riga a parte: 790.305 installazioni al mese da un repository fermo da 905 giorni, in una categoria in cui restare aggiornati è il lavoro stesso. Il comportamento TLS dei browser cambia; una libreria che smette di seguirlo all’inizio del 2024 opera su ipotesi dell’inizio del 2024.
Due avvertenze su questa tabella. I conteggi dei download del registry includono esecuzioni CI e mirror e non fanno deduplicazione, quindi misurano il volume di installazioni, non gli umani. Inoltre le finestre mobili non condividono una data di fine — quelle di npm arrivano al 2026-07-24, mentre quelle di pypistats sono relative al momento del fetch — quindi il totale è la somma di mesi leggermente sfalsati e va letto come “circa 2,28 milioni”, non al millesimo.
Il caso di collisione di nomi che vale la pena conoscere
internetarchive/wayback è il vecchio OpenWayback in Java, fermo da 1.916 giorni. wayback su PyPI è un progetto completamente diverso — edgi-govdata-archiving/wayback — ed è sano, avendo pubblicato la versione 0.5.1 il 2026-06-19, cinque settimane prima della data di riferimento. Stesso nome, condizione opposta, nessuna relazione. Questa è stata una delle sei attribuzioni scartate, ed è quella che più facilmente può colpire un utente reale: cercando il nome si trovano entrambi, e nelle due pagine non c’è nulla che dica quale dei due si è aperto.
Archiviato, deprecato, e ancora installato 127.232 volte al mese
Sei repository del campione hanno archived: true, che GitHub mostra con un banner a tutta larghezza. La mia prima lettura era che questo dimostrasse che le persone ignorano i warning evidenti. La cache racconta una storia peggiore.
Riferimento ufficiale: documentazione dell’API dei download count di npm.
Riferimento ufficiale: documentazione npm sulla deprecazione.
@modelcontextprotocol/server-puppeteer registra 127.232 installazioni al mese da modelcontextprotocol/servers-archived. Ma il campo repository di npm è null — non esiste un link dalla pagina del package al repository, quindi non si tratta di un banner che l’utente ha semplicemente ignorato. La maggior parte di chi scarica questo package non aveva nemmeno un percorso diretto verso quel repository.
Ciò che npm pubblica davvero è la deprecazione. L’ultima versione del package include deprecated: "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." — che npm stampa nel terminale durante l’installazione. Quindi l’avviso arriva davvero, nel punto in cui l’utente si trova, eppure 127.232 installazioni al mese continuano lo stesso. È un risultato più forte del solo banner e sposta il problema altrove: il segnale non manca, arriva dentro un muro di output di installazione che nessuno è obbligato a leggere.
browserbase/mcp-server-browserbase mostra come appare una dismissione pulita: il suo ultimo commit sul branch predefinito, del 2026-07-20, dice letteralmente "Mark repository as archived and unmaintained (#198)". I maintainer l’hanno dichiarato, datato e segnalato nell’API. Le sue 20.389 installazioni meritano comunque attenzione — la finestra npm va dal 2026-06-25 al 2026-07-24, quindi 26 di quei 30 giorni precedono il commit di archiviazione. Quel numero riflette soprattutto la domanda precedente all’annuncio, non la disobbedienza al messaggio. Cosa succede dopo non è davvero deducibile da questo snapshot, e vorrei un secondo riscontro tra un mese prima di tirare conclusioni.
57 stelle, 204.025 installazioni al mese
Janpot/microdata-node ha 57 stelle e riceve 204.025 download al mese da una release datata 2020-05-11.
Con 57 stelle, microdata-node ha una visibilità del repository molto bassa rispetto al suo volume sul registry. Il rapporto installazioni/stelle di 3.579 a 1 è compatibile con uso transitorio, ripetizioni in CI, mirror o consumo diretto da parte di macchine. Questa analisi non ha raccolto grafi di dipendenze e non può scegliere tra queste spiegazioni.
La riga è un invito a verificare l’esposizione indiretta, ma dimostrare l’uso transitivo richiede evidenze di reverse dependency o lockfile che questo audit non ha raccolto.
Stantio non significa rotto
Un audit onesto deve dirlo chiaramente: niente di tutto questo misura se qualcosa sia rotto. Misura se qualcuno è ancora a bordo.
Alcuni di questi progetti sono semplicemente finiti. SetSimilaritySearch implementa algoritmi di similarità tra insiemi; quelli non si degradano. simhash è un paper del 2007. L’algoritmo di estrazione contenuti di boilerpipe nel 2026 si comporta come nel 2015 — qualunque sia la sua precisione sulle pagine moderne, il codice non ti è scivolato via da sotto i piedi.
Ciò che invecchia male è tutto ciò che ha un bersaglio mobile dall’altra parte:
- Automazione browser — ogni release di Chrome può romperla.
- Comportamento di client HTTP che replica i browser reali — i browser cambiano, e una libreria congelata smette di allinearsi;
tls-clientrientra qui. - Parser specifici per sito e regole di estrazione per singolo sito — ogni redesign del sito è un bug.
- Qualsiasi wrapper per API di terze parti — il vendor cambia lo schema e te ne accorgi in produzione.
- Qualsiasi wrapper per un LLM — le deprecazioni dei modelli corrono più veloci di tutto il resto.
Quindi “1.606 giorni di staleness” è un allarme rosso per una categoria e quasi irrilevante per una utility di hashing. Qui non è stato testato alcun malfunzionamento e non si fanno affermazioni in merito; ordinare le dipendenze in base alla categoria di rischio non costa nulla e aiuta più del solo numero di staleness.
I quattro controlli che rispondono davvero alla domanda

Nessuno di questi è l’header del repository.
| # | Controllo | Dove leggerlo | Cosa intercetta |
|---|---|---|---|
| 1 | L’ultimo commit sul branch predefinito | GET /repos/{owner}/{repo}/commits?sha={default_branch}&per_page=1 | Il numero che non ti viene mostrato. |
| 2 | L’ultima volta in cui l’artefatto è stato pubblicato | pypi.org/pypi/{pkg}/json oppure registry.npmjs.org/{pkg} → versione più recente e data di upload | È questo che fa emergere newspaper3k, cosa che il controllo 1 non vedrà mai. Già che ci sei, leggi il campo deprecated di npm, cioè il modo in cui @modelcontextprotocol/server-puppeteer si presenta. |
| 3 | Il flag archived | Un campo nella risposta del repository, chiaro e inequivocabile | Nota che aiuta solo se sei già arrivato al repository, cosa che i package con repository: null non permettono. |
| 4 | Il divario tra 1 e 2 | — (derivato dai due punti sopra) | Un repository con commit recenti ma release vecchia di tre anni è un problema diverso da uno semplicemente fermo: significa che il maintainer è presente ma non sta rilasciando. È una decisione da prendere con gli occhi aperti, non un campanello d’allarme di per sé. La stessa logica vale al contrario per crawlab: verifica se il lavoro si è spostato su un branch non predefinito prima di trarre conclusioni. |
Esegui i controlli 1, 2 e 3 come triage rapido. Poi approfondisci quando i metadati del repository mancano, l’attribuzione del package è ambigua, l’attività si è spostata su un branch non predefinito o l’artefatto del registry diverge dal repository. I limiti di GitHub senza autenticazione e la latenza del registry rendono inopportuna qualsiasi promessa di risposta in un secondo.
Se un controllo segnala qualcosa di vecchio in una categoria che evolve rapidamente, le alternative mantenute in questo spazio sono documentate nei nostri test: Trafilatura per l’estrazione contenuti che dragnet e boilerpipe svolgevano un tempo, Scrapy o Crawlee per i framework di crawling, Crawl4AI e Firecrawl per l’estrazione orientata agli LLM, e Scrapling quando la resilienza conta. Il nostro pilastro sugli scraper open source tiene traccia del panorama più ampio. Si tratta di recensioni di prima mano; esegui tu i quattro controlli prima di fidarti di qualsiasi raccomandazione, compresa la nostra.
Limiti di questi dati
- Selezione del campione. Scelto manualmente perché sospettato di abbandono. Da qui non si possono ricavare tassi dell’ecosistema.
- Nessun test di rottura. Nessuno di questi 35 strumenti è stato eseguito su un sito live. La staleness è un segnale di manutenzione, non un verdetto funzionale.
- I download includono macchine. CI, mirror, nessuna deduplicazione e finestre che non condividono una data di fine. Volume di installazioni, non utenti, e non decisioni.
- Le sette cause sconosciute restano sconosciute. Il feed di attività non ha restituito nulla, e per cinque delle sette la retention non lo spiega. Lasciare la cella vuota è meglio che riempirla con l’ipotesi più comune.
staleness_daysusa le date del committer. Una history riscritta o retrodatata lo altererebbe. Non è stato rilevato nulla, che non equivale a dire che non esista.- Una sola data di riferimento. 2026-07-27. Molti di questi repository saranno cambiati quando leggerai questo testo —
newspaperin particolare fa commit regolarmente. Riesegui i quattro controlli; non citare le mie date.
Dove un servizio gestito cambia la forma del problema
Ogni controllo qui esiste perché, con una libreria self-hosted, la staleness è tua. Se un client HTTP congelato smette di comportarsi come un browser attuale, l’incidente è tuo, in qualunque momento emerga.
Nota dell’autore: Thunderbit è il nostro prodotto gestito per lo scraping. Un servizio gestito trasferisce parte della responsabilità di manutenzione al vendor, ma copertura, tempi di risposta, lock-in e continuità del fornitore entrano a far parte del modello di rischio. Thunderbit non è stato valutato in questo audit sui repository.
Il compromesso onesto è questo: rinunci alla possibilità di leggere il sorgente, bloccare una versione e correggerla da solo alle 2 di notte. Per un team che sta già mantenendo scraper, la strada open source è spesso quella giusta — i quattro controlli servono a trasformare questa scelta in una decisione, non in un’ipotesi.
Prova Thunderbit per l’estrazione di dati dal web
Versione breve
Ho costruito una lista per dimostrare che GitHub nasconde l’abbandono. L’illusione è reale in 14 repository su 35 ed è clamorosa in un caso: sjdirect/abot mostra un push della settimana scorsa contro un branch predefinito fermo dal 2021, cioè un divario di 1.802 giorni.
Ma il meccanismo è più stretto della storia. Nove repository superano tutte le condizioni richieste dall’ipotesi, e solo due di quei nove — splash e geziyor — hanno evidenza positiva che siano stati i bot a gonfiare il timestamp; gli altri passano per assenza di evidenza. Due repository segnalati mostrano esseri umani che tentano e falliscono di far rivivere un progetto. Uno, crawlab, ha 12.250 stelle e ha semplicemente spostato lo sviluppo su develop. In sette casi il feed di attività è vuoto e la retention ne spiega solo due, quindi la causa non è stabilita ma solo ipotizzata. E per 21 repository su 35, GitHub ha riportato correttamente lo stato di staleness.
Il caso peggiore del dataset supera tutti i controlli a livello di repository. codelucas/newspaper è stato committato il 2026-07-21; newspaper3k, pubblicato l’ultima volta il 2018-09-28, è stato scaricato 813.513 volte quel mese. Nel campione, 16 package con release più vecchie di un anno valgono circa 2,28 milioni di installazioni al mese.
Controlla il commit del branch predefinito, la data di pubblicazione del registry, il flag archived e il campo di deprecazione npm come triage iniziale. Poi verifica l’attribuzione package-repository e lo sviluppo su branch non predefiniti prima di trarre conclusioni.
Prova Thunderbit per l’estrazione di dati dal web Get Started Free
FAQ
Che cos’è pushed_at e perché non significa “ultimo aggiornamento”?
pushed_at è il campo dell’API GitHub dietro al timestamp di attività visibile nella pagina del repository, e si aggiorna quando viene fatto un push su qualsiasi branch. L’ultimo commit sul branch predefinito è uno dei segnali di manutenzione del repository, mentre i package manager installano normalmente artefatti del registry o versioni di moduli risolte. In questo audit, 14 repository su 35 mostravano i due timestamp GitHub separati da oltre 180 giorni.
È sempre dependabot a gonfiare quel timestamp?
No, e alla fine questa è risultata la parte più debole della versione popolare della storia. Considerando solo gli eventi avvenuti dopo l’ultimo commit sul branch predefinito, i bot sono confermati in 3 dei 14 repository segnalati (splash 4 su 4, geziyor 5 su 5, any23 16 su 16). In 2 casi l’ipotesi è proprio sbagliata: il divario di dragnet deriva da un umano che ha pushato un port Python 3.10 non mergiato. Altri due sono misti, incluso crawlab, dove 24 push umani sono finiti su develop e test mentre main restava fermo. E per 7 repository il feed di attività non ha restituito nulla, quindi la causa non è stabilita — la retention spiega solo due di quei sette.
Un repository vecchio significa che lo strumento è rotto?
Non sulla base di queste evidenze — qui non è stato eseguito nulla su un sito live. La staleness conta in proporzione a quanto velocemente cambia il target: automazione browser, client HTTP che imitano il comportamento dei browser, parser specifici per sito e wrapper API/LLM invecchiano in fretta, mentre librerie algoritmiche come SetSimilaritySearch o simhash possono avere anni di vita ed essere perfettamente valide. tls-client è il caso più netto del campione, con 790.305 installazioni al mese da un repository fermo da 905 giorni.
Come può un repository essere attivo mentre il package è morto?
È il caso di codelucas/newspaper, ed è la cosa più rilevante emersa da questo audit. Il suo branch predefinito è stato committato il 2026-07-21, pochi giorni prima della data di riferimento, ma newspaper3k su PyPI ha pubblicato l’ultima volta 0.2.8 il 2018-09-28 — 2.858 giorni — e continua a ricevere 813.513 download al mese. I controlli a livello di repository sono tutti superati; l’artefatto che installi è vecchio di otto anni. Controlla sempre separatamente la data di pubblicazione più recente del registry e il log dei commit.
I repository archiviati risolvono il problema? GitHub mostra un banner.
Non in modo affidabile, e @modelcontextprotocol/server-puppeteer spiega perché. Il suo campo repository di npm è null, quindi non c’è alcun link dal package al repository archiviato e nessun banner da saltare. Ciò che npm mostra davvero è la stringa deprecated del package — "Package no longer supported" — stampata in fase di installazione, e 127.232 installazioni al mese passano comunque oltre. browserbase/mcp-server-browserbase ha annunciato correttamente la chiusura nel suo ultimo commit; le sue 20.389 installazioni precedono in gran parte quel commit, quindi dicono poco in un senso o nell’altro.
Come controllo rapidamente le mie dipendenze?
Inizia con la data dell’ultimo commit sul branch predefinito, la data/versione pubblicata nel registry, il campo di deprecazione npm e il flag archived del repository. Poi verifica l’attribuzione package-repository, controlla i branch non predefiniti quando l’attività diverge e usa dependency graph o lockfile prima di considerare l’esposizione come transitiva. Collisioni di nome come i due progetti non correlati wayback su Java e su PyPI rendono necessario questo ulteriore passaggio.


