Colophon / la sala macchine, aperta
L'architettura giusta è quella dimensionata sul problema.
Il problema qui è preciso: un archivio personale che deve essere veloce, costare zero, farsi leggere da umani, crawler e agenti, e lasciarsi mantenere in autopilot. Da lì discende tutto il resto di questa pagina.
Requisiti → decisioni
HTML statico servito da CDN, client JS solo dove c'è interazione. Il collo di bottiglia dev'essere la lettura, mai il caricamento.
Nessun server, nessun database, nessun CMS: niente da patchare, niente che si buca, niente che chiama alle 3 di notte. Un archivio personale non deve chiedere attenzione operativa.
Build statica su piano hobby: il costo marginale di una visita è ~zero, e il costo fisso pure. I soldi vanno nei contenuti, non nell'hosting.
Il sito si mantiene da solo: io dico cosa serve, code-os (si apre in una nuova scheda) lo costruisce, il gate decide se si spedisce. agent-memory (si apre in una nuova scheda) con le regole e la definition of done, decisioni registrate come ADR: una sessione agentica nuova è produttiva al primo prompt, senza di me a fare da memoria.
Anche l'architettura dei dati è per gli agenti: dati tipizzati in src/content con path prevedibili, un componente per responsabilità, nomi che dicono cosa fanno. Qualsiasi cosa si trova senza bruciare token in esplorazione: il contesto minimo sufficiente è una proprietà del repo, non del prompt.
Statico è crawlabile per definizione, e uso Next per quello che sa fare: Metadata API per canonical e OpenGraph, sitemap e robots tipizzati, OG image generate a build time con ImageResponse, font self-hosted via next/font (zero layout shift), JSON-LD nei layout. Per chi cerca via AI: llms.txt e /api/dossier, dalla stessa fonte delle pagine.
Niente funnel, niente popup, niente tracker di terzi (analytics cookieless e basta). È un archivio da leggere, non una landing da convertire.
// anche questa pagina: ho detto cosa volevo, l'harness l'ha costruita, il gate l'ha approvata.
Architettura
CONTENT / SSoT
src/content/*.ts — essay, principi, work: dati tipizzati, 3 lingue
REACT SERVER COMPONENTS
route thin, sezioni SRP, client JS solo dove interagisci
STATIC BUILD
ogni route è HTML a build time — anche OG, llms.txt, JSON
EDGE / CDN
Vercel — zero compute a runtime, costo marginale ~0
lo stesso contenuto esce da /llms.txt e /api/dossier
GATE
- eslint
- tsc --noEmit
- next build
verde, o non si committa
INVARIANTI
- a11y AA
- reduced motion
- un solo accento
- SSoT-derived
- parità i18n
regressione = bug
Decision record
Zero dipendenze, più una
Runtime: Next + React + react-aria-components (accessibilità dei widget: focus management, press semantics, focus trap — cose che sbagli se le riscrivi). Niente Tailwind, CMS, UI kit, librerie di animazione. Conseguenza: superficie di update minima, ogni riga in bundle è giustificabile.
Tutto statico, sempre
Ogni route — HTML, OG image, llms.txt, /api/dossier — è generata a build time e servita da CDN. Nessun compute a runtime: niente cold start, superficie d'attacco ridotta al minimo, costo marginale per request ~zero. Il vincolo è accettato: contenuto nuovo = nuovo deploy.
Il contenuto è un tipo, non una pagina
Essay, principi, work vivono in src/content come oggetti tipizzati (SSoT). Archivio, homepage, sitemap, OG, llms.txt e /api/dossier derivano dalla stessa fonte. Conseguenza: un contenuto si scrive una volta e non può divergere tra i formati.
Un solo accento
#E35B2D, ovunque; sulla neve una sfumatura più scura (#B8481C) per il contrasto AA su testo piccolo — stessa tinta, non un secondo colore. Vale anche negli easter egg: una nevicata ha esattamente un fiocco arancione.
Motion come decorazione, mai come gate
Lo stato nascosto dello scroll-reveal esiste solo con JS attivo (@media scripting) e motion consentito. JS spento o prefers-reduced-motion: contenuto integro. Le animazioni sono un layer sopra il contenuto, non una precondizione per leggerlo.
Accessibilità come invariante
Skip-link con target focusabile, focus ring visibile, aria-current sulla navigazione, contrasti AA, link esterni annunciati, aria-live anche per gli easter egg. Trattata come i test: una regressione è un bug, non un compromesso.
Leggibile dalle macchine
/llms.txt per gli agenti, /api/dossier per curl, JSON-LD per i crawler — tutti generati dallo stesso SSoT delle pagine, quindi mai disallineati. Il contesto su di me lo servo io, nel formato di chi lo consuma.
Tre lingue, un SSoT
IT, EN, PL come route statiche (/it /en /pl), hreflang per pagina, slug condivisi. Niente middleware: la detection è un redirect statico. La parità tra lingue è verificata a build time: una traduzione mancante rompe la build, non la fiducia.
Per le macchine
Il contenuto su di me lo servo io, nel formato di chi lo consuma. Gli agenti leggono /llms.txt; chi preferisce il terminale ha un endpoint JSON generato dallo stesso SSoT delle pagine:
curl simonebova.com/api/dossier
// stessa fonte, tre formati: HTML per te, llms.txt per gli agenti, JSON per curl.