Files
InsanityLab-web/.superpowers/sdd/2026-08-22-longevity-interfaccia/task-1-brief.md
T
Adriano b913a2bf90 registri dei piani in git (AI-OS#309)
Refs gitea:Adriano/AI-OS#310

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:36:26 +02:00

4.4 KiB

Task 1: Il guscio — layout, stile, e una rotta che risponde

Files:

  • Create: src/layouts/Longevity.astro, src/styles/longevity.css, src/pages/longevity/index.astro
  • Test: tests/longevity/pagine.test.ts

Interfaces:

  • Consumes: isProtectedPath, canAccessAdminPath da src/lib/auth.ts
  • Produces: il layout Longevity.astro con title e crumbs, e le classi .lg-*

Il modello da seguire: src/layouts/StressIndex.astro e src/styles/stress-index.css. Leggili prima di scrivere. Il foglio di stile va globale e prefissato, non in <style> scoped: le stesse classi servono ai componenti Astro e alle isole React, che lo scope di Astro non raggiunge — la ragione è scritta in testa al foglio di Stress Index.

La differenza voluta rispetto a Stress Index. Quella piattaforma ha una barra laterale con sette voci, giusta per chi ci lavora dentro. Qui i pubblici sono due e uno non è staff: il cliente entra tre o quattro volte l'anno per guardare il proprio referto, e una barra di navigazione è l'arredamento di uno strumento di lavoro. Quindi il layout prevede due forme: variante="referto" (una colonna che scorre, nessuna navigazione) e variante="gestionale" (barra laterale come Stress Index). Questo task realizza la prima; la seconda arriva col piano del gestionale.

  • Step 1: Write the failing test
// tests/longevity/pagine.test.ts
import { describe, it, expect } from 'vitest';
import { readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';

const leggi = (p: string) => readFileSync(join(process.cwd(), p), 'utf8');

describe('il guscio della piattaforma', () => {
  it('il layout esiste e non monta header e footer pubblici', () => {
    const l = leggi('src/layouts/Longevity.astro');
    expect(l).not.toMatch(/import\s+Header/);
    expect(l).not.toMatch(/import\s+Footer/);
    expect(l).toMatch(/noindex/);          // area riservata: mai indicizzata
  });

  it('il foglio di stile e globale e prefissato, non scoped', () => {
    expect(existsSync(join(process.cwd(), 'src/styles/longevity.css'))).toBe(true);
    const css = leggi('src/styles/longevity.css');
    const classi = [...css.matchAll(/^\.([a-z-]+)/gm)].map((m) => m[1]);
    expect(classi.length).toBeGreaterThan(3);
    expect(classi.every((c) => c.startsWith('lg'))).toBe(true);
  });

  it('usa i token del sito invece di inventare colori', () => {
    const css = leggi('src/styles/longevity.css');
    expect(css).toMatch(/var\(--c-/);
  });

  it('nessun semaforo sui punteggi: il colore non giudica il corpo', () => {
    const css = leggi('src/styles/longevity.css').toLowerCase();
    // il mattone del fuori range e ammesso; un verde "tutto bene" no
    expect(css).not.toMatch(/--lg-ok|--lg-buono|--lg-verde/);
  });

  it('la rotta del cliente e sotto /longevity, dove le regole la proteggono', () => {
    expect(existsSync(join(process.cwd(), 'src/pages/longevity/index.astro'))).toBe(true);
  });
});
  • Step 2: Run test to verify it fails

Run: npm test -- tests/longevity/pagine.test.ts Expected: FAIL — i file non esistono

  • Step 3: Write minimal implementation

Il layout prende title, crumbs e variante, importa i font e i due fogli di stile (global.css e longevity.css), e dichiara noindex. Nessun Header/Footer pubblico: la navigazione è interna.

Il foglio longevity.css definisce, tutte prefissate lg: il guscio del referto (una colonna centrata, larghezza leggibile), l'intestazione, la carta di un punteggio, e i token locali. Le variabili locali riprendono quelle del sito (--c-accent, --c-dark, --c-bg-alt) e ne aggiungono due sole:

--lg-tratteggio: #d8d2c8;   /* il perimetro di cio che non sappiamo */
--lg-fuori-range: #b05a4e;  /* SOLO per una misura sospetta, mai per un punteggio */

⚠️ Non definire un colore «buono» o «cattivo» per i punteggi: l'intensità si ottiene variando l'opacità dell'accento. È la regola di prodotto, e il test la controlla.

src/pages/longevity/index.astro reindirizza a /longevity/io.

  • Step 4: Run test to verify it passes

Run: npm test -- tests/longevity/pagine.test.ts → PASS (5 test) Poi: npm test per intero e npx tsc --noEmit.

  • Step 5: Commit
git add src/layouts/Longevity.astro src/styles/longevity.css src/pages/longevity/index.astro tests/longevity/pagine.test.ts
git commit -m "longevity: il guscio della piattaforma, senza semaforo sui punteggi"