Parto dalla parte che non mi fa fare bella figura.
Nelle mie lezioni su Webflow ho pubblicato nove identificatori che non esistono. Non errori di battitura: nomi. Un prefisso di attributo sbagliato in tutte e sedici le volte in cui compariva. Una variabile CSS su cui qualcuno avrebbe passato una serata. Un valore ARIA con una lettera maiuscola al posto sbagliato, che spegne in silenzio esattamente la cosa che dovrebbe far funzionare.
Erano tutti sbagliati nello stesso modo: non li ho copiati dal progetto. Li ho ricostruiti da una trascrizione di me che parlavo del progetto.
Poi ne ho trovati altri otto prima che uscissero, e li ho trovati solo perché avevo smesso di fidarmi di me e avevo scritto un check che fa fallire la build. Questo è quanto costa e come funziona.
La versione breve
- Ho 73 lezioni in 12 corsi, in inglese e italiano, e la maggior parte è nata da trascrizioni di me che parlo. È un buon modo per non perdere la voce. È un modo terribile per azzeccare un nome.
- Un giro di audit su 21 lezioni ha trovato nove identificatori sbagliati. I giri precedenti ne avevano già trovati altri, quindi nove è un passaggio, non un totale.
- Nessuno dei nove era un errore che avrei potuto prendere rileggendo. Erano tutti plausibili: nomi che suonano giusti, derivati da una convenzione vera.
- Costruendo una knowledge base da quelle lezioni ho rischiato di pubblicare otto nomi di attributo inventati in un colpo solo, ragionando correttamente su uno schema che quel componente rompe.
- Così ho scritto la regola — gli identificatori si copiano dal progetto vero, non si ricordano — e poi l’ho fatta imporre dalla build: una pagina che porta nomi specifici del progetto citando come fonte solo la prosa fallisce. Non un avviso. Una build rotta.
- Il check ha preso due errori di provenienza veri al primo giro, e un falso positivo che mi ha insegnato qual era davvero la regola.
- E ha un punto cieco che posso dimostrare, perché ha promosso una lezione che non nominava nessun identificatore.
Nove nomi, e perché nessuno sembra un errore
Questa è la tabella che ho nei miei appunti. A sinistra quello che è uscito. A destra quello che c’è davvero nel progetto.
| Quello che ho pubblicato | Quello che è in realtà |
|---|---|
fc-fluid-gradient-gooey | fc-fluid-gradient-gui |
FC-drop-* | fc-dropdown-* — tutto il prefisso, in 16 punti per lingua |
fs-mirror-input-element | fs-mirrorinput-element |
var(--glow) | var(--glow-color) |
aria-label="breadcrumb" | aria-label="Breadcrumb" |
classe toc-sub-items | classe toc-subitems |
FC-ripples* | fc-ripples* — 17 occorrenze per lingua |
FC-marquee-* | fc-marquee-* |
| range di tuning in pixel per un filtro SVG | frazioni — il filtro usa primitiveUnits="objectBoundingBox" |
Guarda un attimo il primo, che è il mio preferito e il meno difendibile. Quel componente ha un piccolo pannello di controllo, e nel video ho detto “GUI”, ad alta voce, come una persona normale. La trascrizione ha sentito gooey. E fc-fluid-gradient-gooey si legge benissimo — è un gradiente fluido, gooey è una parola che ti aspetti da quelle parti, ed esiste un effetto Webflow molto noto che si chiama proprio gooey blob. Niente in quella stringa chiede di essere verificato.
Il quarto è quello che come lettore mi darebbe più fastidio. --glow è un nome migliore di --glow-color. È più corto, è quello che scriverei oggi, e non è quello che c’è nel file. Uno copia quella riga, non ottiene nessun glow, nessun errore, nessun avviso in console, e conclude ragionevolmente di aver sbagliato lui.
Il quinto è quello davvero pericoloso. aria-label="breadcrumb" invece di "Breadcrumb": i selettori di attributo in CSS distinguono maiuscole e minuscole, e i separatori tra i link del breadcrumb li disegna nav[aria-label="Breadcrumb"]::after. Pubblica la versione minuscola e ogni separatore sparisce — su un componente di accessibilità, in una lezione sull’accessibilità.
E due riguardano solo delle maiuscole: FC- dove il progetto usa fc-. Che sembra la voce più banale della tabella, finché non ti ricordi che chi segue la lezione digita quello che legge.
Il motivo per cui suonavano tutti giusti
Sotto tutti e nove c’è un solo meccanismo, e non è la sbadataggine.
Una trascrizione conserva quello che intendevo e butta via come si scrive. Il parlato non ha maiuscole, né trattini, né underscore. “effe ci trattino dropdown” e “FC punto drop” sono lo stesso suono con conseguenze diverse. Così, trasformando una trascrizione in una lezione, il significato arrivava intatto e ogni identificatore arrivava come impressione di un nome — e per chiudere la frase dovevo indovinare come si scriveva.
Indovinare non è la sensazione che dà. La sensazione è quella di ricordare. E le ipotesi sono buone, perché nascono da una conoscenza vera delle mie convenzioni: so che prefisso gli attributi dei componenti con fc-, quindi fc-dropdown-behavior esce con sicurezza totale. E per caso è giusto. FC-drop-behavior è uscito con la stessa sicurezza ed era sbagliato sedici volte.
Se ti suona familiare, è normale. È esattamente ciò che fa un assistente AI quando risponde su una libreria di cui ha letto parlare invece di averla letta. Che è come sono finito dall’altro lato dello stesso problema.
Gli otto che non sono usciti
Mentre costruivo una knowledge base a partire dalle 23 lezioni su GSAP, una pagina mi ha bloccato: il componente stagger. La sua lezione spiega correttamente il comportamento di ogni opzione e non dice mai cosa scrivere. Nove opzioni descritte, zero nomi di attributo sulla pagina.
Così ho fatto quello che farebbe qualunque sistema competente: li ho dedotti. Ogni altro componente nel mio corpus segue la stessa forma, fc-<componente>-<opzione>. Nove opzioni, nove nomi:
fc-gsap-staggered-threshold
fc-gsap-staggered-each
fc-gsap-staggered-amount
…
Sicuri, coerenti, derivati da una convenzione che esiste davvero in una dozzina di componenti.
Tutti sbagliati. Questo è il vero, preso dal cloneable:
fc-gsap-staggered="list"
threshold="0.4" each="0.2" amount="0.5" duration="0.5"
offset-y="3rem" from="start" ease="power3.out" staggered-ease="power1.out"
Solo il marker è prefissato. Le opzioni sono nude. fc-gsap-staggered-each non esiste e non è mai esistito. Un componente, uno solo, rompe lo schema — ed è proprio quello la cui lezione si è dimenticata di scrivere i nomi.
Sono otto identificatori inventati, prodotti da un ragionamento corretto su una convenzione vera. Non una trascrizione sbagliata, non un’allucinazione nel senso sciatto del termine: un’inferenza valida su uno schema che ha un’eccezione dentro. È il modo di sbagliare che trovo davvero difficile da difendere, perché nel processo era tutto solido tranne il risultato.
Se preferisci vederlo succedere invece di leggerlo, è l’apertura di un video che ho fatto sullo stesso problema — un assistente che nomina con totale sicurezza un attributo di un mio componente che non è mai esistito. È in inglese.
E la stessa pagina aveva altri due errori di natura diversa, che falliscono senza nessun messaggio d’errore:
- La lezione diceva
offsetY. L’attributo vero èoffset-y. L’HTML mette in minuscolo i nomi degli attributi, quindioffsetYarriva allo script comeoffsety, non combacia con niente e ti lascia in silenzio sul default3rem. L’animazione funziona. Solo che ti ignora. - Il secondo easing dello stagger era descritto come “l’ease dello stagger” e mai nominato. Si chiama
staggered-ease. Non puoi digitare un nome che nessuno ha scritto.
La regola, in una riga
Gli identificatori si copiano dal progetto vero. Mai ricordati, mai ricostruiti, mai dedotti da una convenzione — nemmeno da una convenzione che ho inventato io.
Tutto il resto di questo articolo è macchinario per far sopravvivere quella regola all’incontro con un autore stanco alla quarantesima pagina. Perché una regola che devi ricordarti nel momento in cui è meno probabile che te ne ricordi non è una regola: è una speranza.
Il check che fa fallire la build
La knowledge base ha 42 pagine e ognuna dichiara da dove viene il suo contenuto, in un campo che si chiama verified_against, con quattro valori ammessi in ordine di forza decrescente:
cloneable— copiato dal progetto Webflow vero. Il più forte, e l’unico che può portare onestamente un blocco di codice.designer— per i fatti che esistono solo come interfaccia di Webflow, dove non c’è nessun file da cui copiare.author— per i fatti che non stanno in nessuna lezione e in nessun documento, solo nella mia testa. Questo livello l’ho dovuto inventare a metà strada, e già quello era un’informazione: le lezioni sono dense ma non esaustive sul proprio ragionamento.lesson-prose— derivato dal testo di una lezione. Va bene per spiegare. Non è accettabile per un nome.
Poi c’è uno script che gira dopo ogni build, accanto ai gate su accessibilità e compliance, e una delle sue regole è tutto il punto:
Una pagina la cui unica fonte è
lesson-prosenon può portare un blocco di codice con identificatori specifici del progetto, né una tabella di attributi.
Se lo fa, la build fallisce. Non un avviso in un log che nessuno legge: un exit code. Il ragionamento era che un identificatore inventato in un corpus scritto per gli assistenti AI è peggio di un link rotto su una pagina che legge una persona, perché chi legge non ha modo di accorgersi che è sbagliato. Un link rotto si annuncia da solo. Un nome di attributo plausibile no.
Due cose hanno reso la scrittura più difficile di quanto sembri.
Lo schema non ha valori di default, da nessuna parte. Se una pagina ha bisogno di una lista vuota, chi scrive deve digitare la lista vuota. Sembra pedanteria ed è la parte portante: un campo con un default sensato è un campo che puoi dimenticare, e un campo di provenienza dimenticato significa in silenzio “fidati di me”.
“Identificatore specifico del progetto” andava definito, e la prima volta l’ho sbagliato. La mia prima versione segnalava height: 100dvh — CSS standard, nelle specifiche di ogni browser, non roba mia da verificare. Un falso positivo al primo giro, e utile: mi ha costretto a fare la distinzione. Ora il check cerca nomi che esistono solo dentro i miei progetti veri — le famiglie di attributi fc-*, gli attributi data-* di targeting, le custom property dichiarate, i selettori di classe definiti, l’handle del runtime delle interazioni — non i blocchi di codice in quanto tali. Il rischio non è il codice. Il rischio è un nome che qualcuno potrebbe plausibilmente inventarsi.
Cosa ha preso subito
Al primo giro vero il check ha fatto fallire la build due volte, su pagine che avevo scritto e riletto io:
- La pagina dello stagger portava quella tabella di nove righe — quella giusta, quella presa dal cloneable — mentre il campo della fonte diceva ancora
lesson-prose. I nomi erano corretti e la provenienza era una bugia. Che conta, perché la prossima persona che modifica quella pagina non ha modo di sapere che la tabella era stata verificata. - La pagina sul controllo della timeline portava uno snippet con l’handle del runtime delle interazioni, copiato dal cloneable, con la stessa dichiarazione sbagliata su da dove venisse.
Entrambe risolte dicendo la verità: ora la fonte è una lista, [cloneable, lesson-prose], perché una pagina vera di solito ne ha due.
Nessuno dei due errori avrebbe fatto male a un lettore oggi. Entrambi avrebbero reso la pagina non verificabile dopo, che per un documento scritto per essere citato dalle macchine è la stessa cosa che essere sbagliato, solo più lentamente.
Cosa il check non vede, e posso dimostrarlo
Questo è il limite, e preferisco dirtelo io piuttosto che lasciartelo scoprire.
La regola verifica solo gli identificatori presenti. Una lezione che non nomina nessun identificatore passa liscia: non c’è niente da controllare. Ed è esattamente quello che è successo — un giro di audit ha marcato la lezione dello stagger come completa, e non lo era. Si legge come completa. Spiega correttamente il comportamento di ogni opzione e non dice mai cosa scrivere. Il buco è sopravvissuto proprio perché sulla pagina non c’era niente di sbagliato: c’era qualcosa che mancava, e ciò che manca è invisibile a un check che guarda ciò che c’è.
Quindi la regola ha bisogno di una domanda gemella, fatta da una persona, ed è imbarazzantemente semplice: questa pagina nomina i suoi identificatori, sì o no? Una versione automatica non ce l’ho. E non sono sicuro che ne esista una utile, perché “questa pagina avrebbe dovuto contenere un nome” è un giudizio sull’intenzione.
Altri due limiti, nello stesso spirito:
verified_against: authornon è verificabile da niente. Vuol dire “lo dice Francesco”. È il livello più forte e il meno controllabile, che è un accoppiamento scomodo. È onesto su chi prendersela, e non è altro che questo.- Un check sul mio repository non dimostra niente sui miei video. I 74 tutorial parlati che hanno dato origine a tutta questa storia non hanno nessun gate di build, e non l’avranno mai.
Se pubblichi qualcosa che un assistente leggerà
Tre cose, nell’ordine in cui le farei.
1. Scopri se i tuoi identificatori sono stati copiati o ricordati. Non se sono giusti: se qualcuno li ha mai verificati. Prendi cinque nomi dalla tua documentazione e prova a dire da dove viene ognuno. Se la risposta è “dall’articolo”, hai lo stesso problema che avevo io, e non ne conosci ancora le dimensioni.
2. Scrivi la provenienza dentro il file. Un campo, per pagina, senza valore di default. È il passo che costa poco ed è quasi tutto il beneficio, perché un nome di cui è registrata l’origine può essere ri-verificato da qualcuno che non sei tu. Un nome senza origine va ri-dedotto, e ri-dedurre è come si ottiene la seconda generazione di narrativa plausibile.
3. E poi fa’ che rompa qualcosa. Una regola imposta dall’attenzione decade esattamente alla velocità con cui decade la tua attenzione. La mia gira dopo ogni build e ci mette meno di un secondo. Mi ha fatto fallire la build quattro volte e ogni volta gli ho detto grazie, che non è una frase che mi aspettavo di scrivere su un linter.
E la parte che non ha niente a che fare con gli strumenti: quando un nome conta, vai a guardarlo. Apri il progetto. Copia la stringa. Nove dei miei sarebbero sopravvissuti a questa singola abitudine, e anche gli otto che ho quasi pubblicato — perché nel momento in cui ho aperto quel cloneable, lo schema in cui mi ero ragionato dentro è evaporato in circa quattro secondi.
Il meccanismo su cui continuo a tornare è che essere sicuri ed essere corretti sono prodotti da due processi diversi, e solo uno dei due lascia una traccia che puoi controllare dopo. Vale per un assistente che risponde su una tua libreria. Si è rivelato valere allo stesso modo per me, mentre descrivevo la mia.
Le cose di cui parla questo articolo
Il corpus che tutto questo protegge: 42 pagine, ognuna con la sua fonte dichiarata.
Le stesse pagine in Markdown puro, se preferisci installarle nel tuo assistente invece di leggerle:
github.com/francesco-castronuovo/gsap-webflow-skill
E il motivo per cui tutto questo esiste: prima di annunciare qualsiasi cosa ho misurato se gli assistenti AI mandano qualcuno sul mio sito. Dieci domande, quattro assistenti, 31 punti su 80 — e la knowledge base ha fatto zero. Quella storia, compresi gli otto nomi inventati raccontati per esteso, è in Il credito va altrove e nel video. I numeri stanno su una pagina sola che si allunga a ogni round: Credited → — quella è in inglese, come la knowledge base.
E c’è un articolo gemello, preso dall’altro lato: scrivere pagine di cui un assistente si può fidare è metà del problema, quale crawler le legge è l’altra metà. Quello di Anthropic ha letto tutte e 42 queste pagine in un’ora e Claude ha accreditato comunque il mio vecchio dominio — Tre tipi di crawler AI →.
Buon viaggio,
Francesco