CryptoHelvetia Logo
Torna alla raccolta

Skill

CryptoHelvetia

Redigi i Dev Report

Definisce struttura, tono, vincoli di periodo e regole anti-troncamento per i Dev Report di CH Labs.

File

skills/dev-report-editorial.md

Scarica .md

Prompt


name: dev-report-editorial description: Regole editoriali per i Dev Report di CH Labs (report settimanali/periodici basati sui commit). Spiega struttura, tono, regole di periodo, anti-troncamento e dove vivono nel codice.


Skill: Editorial dei Dev Report

A cosa serve

I Dev Report di CH Labs sono articoli giornalistici autogenerati dai commit GitHub di ogni progetto attivo. Vengono generati settimanalmente da un cron Vercel (lunedì 9:00 UTC) o on-demand via API. Questa skill è il riferimento unico per le regole editoriali: tono, struttura, lingua, vincoli di periodo, lunghezza, anti-troncamento.

Regola d'oro: se devi cambiare una regola editoriale, modifica lib/editorial/rules.ts. Mai sparpagliare regex o frasi vietate in altri file.

Dove vive il codice

`` lib/editorial/ rules.ts # SINGLE SOURCE OF TRUTH: sezioni, frasi proibite, tono, lunghezze types.ts # tipi (ReportSection, EditorialRules, PeriodCopy, ...) dates.ts # period copy, formattazione date italiane, calcolo giorni inclusivi prompt.ts # buildSystemPrompt, buildContinuationPrompt, getMaxOutputTokens parse.ts # estrae title/summary/content dal raw markdown dell'AI normalize.ts # applica le forbiddenPhrases dopo l'AI validate.ts # isReportComplete, isSummaryComplete, detectTruncation render.ts # markdownToHtml + helper di formattazione fallback.ts # report deterministico quando l'AI fallisce index.ts # composeReport(): orchestrazione completa con recovery ``

Orchestrazione end-to-end: lib/dev-reports.ts (fetch progetto + commit, costruisce input, chiama composeReport, salva su Supabase).

Pipeline di generazione

  1. Cron o API → generateWeeklyReport(projectId) in lib/dev-reports.ts.
  2. Recupera config progetto da project_github_config (Supabase).
  3. Calcola weekStart/weekEnd con getWeekBoundaries + costruisce PeriodCopy con getReportPeriod.
  4. Fetcha commit GitHub via fetchRecentCommits + README via buildRepoContext.
  5. Costruisce ReportComposeInput + FallbackInput + aiCall (provider Anthropic Haiku, claude-haiku-4-5, via ANTHROPIC_API_KEY; il caller Gemini resta disponibile come alternativa).
  6. Chiama composeReport({input, period, aiCall, fallback}) da lib/editorial:
  • buildSystemPrompt → primo tentativo AI
  • parseAiOutput + detectTruncation
  • se troncato: buildContinuationPrompt + secondo tentativo + merge
  • normalizeReport (forbidden phrases)
  • isReportComplete + isSummaryComplete
  • se ancora incompleto: generateFallbackReport (deterministico)
  1. Salva su dev_reports (Supabase).

Regole editoriali consolidate

Lingua e tono

  • Lingua: italiano.
  • Target: investitori SaaS-oriented, comprensione business ma non necessariamente tech.
  • Tono: editoriale, scorrevole, concreto. Ogni frase supportata da fatti nei commit o nelle metriche.
  • Vietati: hype, claim non dimostrabili, aggettivi promozionali, frasi vaghe ("migliorata la stabilità", "abbiamo lavorato duramente", "rivoluzionario", "best-in-class", "game-changer"). Lista in EDITORIAL_RULES.tone.banned.

Struttura obbligatoria (4 sezioni in quest'ordine)

  1. Cosa abbiamo fatto questa settimana (se periodo = 7 giorni) / Cosa abbiamo fatto nel periodo (altrimenti) — sezione dinamica, gestita da getActivityHeading(period.isWeek).
  2. Highlights tecnici — lista di 2-8 bullet, ognuno una capability o milestone reale.
  3. Progressi — paragrafo con numeri concreti che interpreta le metriche.
  4. Prossimi passi — indicazioni operative coerenti, non auspici generici.

Le sezioni sono dichiarate in EDITORIAL_RULES.sections. Il prompt builder le itera per generare il blocco "STRUTTURA OBBLIGATORIA". Il validatore le itera per verificare presenza.

Regole di periodo (caso "settimanale")

Background: in passato l'AI generava report con frasi tipo "ultima settimana" o "settimanale" anche per intervalli di 30 giorni, perché il prompt diceva "settimana" indipendentemente dalla durata reale.

Soluzione attuale (2 strati):

  1. Prompt-side: se period.isWeek === false, il prompt esplicita "Vietato usare 'settimanale', 'settimanali', 'questa settimana', 'ultima settimana'. Usa '${period.label}' (es. 'periodo di 30 giorni')."
  2. Post-AI normalization: normalizeReport applica EDITORIAL_RULES.forbiddenPhrases con condizione days_neq_7. Anche se l'AI sbaglia, le regex correggono.

Aggiungere una nuova frase vietata: modifica solo EDITORIAL_RULES.forbiddenPhrases in rules.ts. Nessun altro file.

Lunghezza dinamica

  • Contenuto: 1.200-3.500 caratteri. Banda stretta per forzare concisione: l'AI resta vicino al minimo se il volume di lavoro è basso.
  • Summary: 80-240 caratteri, una o due frasi chiuse.
  • Bullet per sezione: 2-6, ognuno su una sola riga.
  • Le soglie vivono in EDITORIAL_RULES.lengths. Non duplicare numeri altrove.
  • getMaxOutputTokens() calcola il cap dell'AI a partire dalla soglia massima (≈ 0.4 token/char per italiano + margine).

Voce: conciso, tecnico, diretto

L'obiettivo è prosa densa e scorrevole, leggibile dal profano ma asciutta. Le direttive vivono in EDITORIAL_RULES.tone.defaults (iterate nel prompt come "REGOLE DI INTERPRETAZIONE") e nel blocco "REGOLE PER IL CONTENUTO" di prompt.ts: una idea per frase, voce attiva, niente preamboli o chiusure retoriche, gergo chiarito in mezza riga, titolo breve (max ~70 caratteri) senza sottotitoli con due punti.

Trattino lungo (— / –) vietato, come da house style. Doppio strato: il prompt lo proibisce e forbiddenPhrases con when: "always" lo sostituisce con virgola anche se l'AI sbaglia.

Filtro di stile linguistico

Sopra le regole di tono vale il filtro di stile della Prompt Library. La fonte unica è il file content/prompt-library/chat-preferences/filtra-stile-linguistico.md: il suo body viene caricato a runtime e iniettato nel system prompt come blocco "FILTRO DI STILE LINGUISTICO". Impone scrittura diretta e affermativa, vieta le parole "tradizione/tradizionale/rituale/simbolico" e gli equivalenti inglesi, "davvero" come intensificatore e "crucial", limita i due punti ai casi indispensabili nella prosa, esclude la virgola di Oxford in italiano e proibisce la negazione contrastiva ("non X ma Y"), da riscrivere in forma affermativa.

Catena di caricamento: getStyleFilterPrompt() in lib/prompt-library.server.ts legge il body del file → lib/dev-reports.ts lo passa a composeReport({ ..., styleFilter }) → buildSystemPrompt lo rende verbatim sotto l'header del blocco. Non esiste una copia delle direttive nel codice: per cambiare il filtro si edita solo il file .md, e la modifica si propaga ai Dev Report e a ogni altra skill del sito CH che chiama getStyleFilterPrompt(). Se il file non si carica, il blocco viene omesso e parte un warning; il trattino lungo resta comunque coperto dal layer post-AI in forbiddenPhrases.

Anti-troncamento

Regola: un report non deve mai uscire troncato. Se accade, è un bug, non un comportamento accettabile.

detectTruncation segna il report come troncato se almeno una di queste è vera:

  • finishReason === "MAX_TOKENS" dalla risposta API
  • ultima frase non chiusa (no ., !, ?, :, ", »)
  • manca una sezione obbligatoria
  • l'ultima riga è un marker di lista vuoto (- da solo)

Recovery (composeReport):

  1. Primo tentativo AI normale.
  2. Se troncato → continuation: si passa il markdown parziale e si chiede "completa da dove ti sei fermato, non ripetere, chiudi le sezioni mancanti".
  3. Se ancora troncato → generateFallbackReport deterministico (per costruzione completo).

Mai troncare a posteriori con slice() per rispettare un limite. Se serve accorciare, è l'AI a riscrivere.

Markdown e rendering

L'AI produce Markdown semplice. parseAiOutput lo estrae, normalizeReportContent (in render.ts) lo converte in HTML che viene salvato su Supabase. Il client lo mostra con dangerouslySetInnerHTML dentro un div.dev-report-content (stile in app/globals.css).

Il parser gestisce: H1 (saltato, il titolo è separato), H2/H3, liste - o , paragrafi separati da riga vuota, bold, italic*, riconoscimento euristico di righe corte dopo : come list items.

Un bullet -/* può essere hard-wrappato su più righe sorgente (singolo a-capo, senza riga vuota): le righe di continuazione vengono accodate allo stesso <li> via il buffer listItem in markdownToHtml. Senza questo, un bold spezzato a metà riga lasciava gli asterischi orfani e buttava la coda fuori dalla lista. Se ricompare un'impaginazione rotta nei bullet, il sospetto è una continuazione di list item non gestita lì.

Date in italiano

Un solo helper: formatItalianDate(dateStr) in lib/editorial/dates.ts. Restituisce "28 aprile 2026". Non duplicare toLocaleDateString("it-IT", ...) altrove.

Cosa fare quando...

...l'utente segnala una frase generica/vaga/banale che ricorre nei report:

  • Aggiungila a EDITORIAL_RULES.tone.banned per renderla esplicita nel prompt.
  • Se vuoi una sostituzione automatica, aggiungi anche una pattern in forbiddenPhrases con when: "always".

...l'utente vuole una nuova sezione obbligatoria:

  • Aggiungi un elemento a EDITORIAL_RULES.sections. Il prompt builder e il validatore la prenderanno automaticamente.

...l'utente vuole cambiare la banda di lunghezza:

  • Modifica EDITORIAL_RULES.lengths. getMaxOutputTokens si ricalibra da solo.

...l'utente vuole cambiare provider AI:

  • I dev report sono fissati su Anthropic Haiku in getDevReportsAIConfig() (lib/ai-config.ts), indipendente dal global AI_PROVIDER. Per cambiare modello/endpoint editare lì.
  • I caller vivono in lib/dev-reports.ts: makeAnthropicCaller e makeGeminiCaller, selezionati per aiConfig.provider. Per un nuovo provider aggiungi un caller che rispetti l'interfaccia AiCaller da @/lib/editorial e mappa il suo stop_reason su "MAX_TOKENS" per far funzionare detectTruncation. La pipeline editoriale resta invariata.

...l'utente vuole testare manualmente:

  • POST /api/ch-labs/generate-weekly-report con body {projectId, force: true, referenceDate?}.
  • Per testare il caso periodo lungo, passa una referenceDate che produca un range > 7 giorni e verifica che il report non contenga "settimana"/"settimanale".

Anti-pattern da rifiutare

  • Aggiungere una replace(/.../, "...") in dev-reports.ts o nei componenti UI. Sempre in rules.ts.
  • Hardcodare un nome di sezione in un controllo. Sempre via getRequiredSectionHeadings(period.isWeek).
  • Tagliare il contenuto con .slice() per "fixare" un troncamento. Sempre via continuation o fallback.
  • Aggiungere una nuova costante di lunghezza fuori da EDITORIAL_RULES.lengths.
  • Duplicare la formattazione date italiane.