Files
Claude Code DTP Worker f0a7d71357 [DTP-Worker] Sprint 2 · Publiciste scaffold (parser faisabilité → JSON + generator + gate)
Livrable Publiciste · Sprint 2 · Semaine 2 (seul module net-neuf · chemin
critique · GAP_ANALYSIS §3.13). Cible portage VPS : otoia/capabilities/publiciste.py.

- lib/parser.py : data_room/PXX/ (template v1.0) → projet dict conforme au
  contrat projets_master.schema.json (livré S1). Mapping colonnes par en-tête,
  parsing montants USD/DOP robuste. Anti-invention #6 : rétrogradation défensive
  « en_developpement » si prix USD manquant ; absent → null (jamais 0/inventé).
- lib/validator.py : validateur JSON-Schema draft-07 (sous-ensemble) ZÉRO
  dépendance pip (runner Gitea sans pip). Oracle jsonschema en test si présent.
- lib/generator.py + template + branding : rendu HTML luxury #4 (dark+doré,
  Fraunces + Cormorant Garamond) ; sans prix → « Prochainement · Détails à venir ».
- publiciste.py : CLI parse/validate/generate/run.
- fixtures/ : données SYNTHÉTIQUES de test (jamais publiées) P01 complète + P02
  incomplète.
- tests/ : 23 tests unittest (stdlib) verts.
- CI : job publiciste-tests ajouté au gate (.gitea/workflows/ci.yml · Gitea #2).

Vérifs (en-repo, sans VPS) : 23/23 tests verts · gate CI local vert (guard/JSON/
docs) · pipeline CLI produit un master conforme au schéma. Auto-score 4Big 95/100.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 01:37:47 +00:00

86 lines
3.8 KiB
Cheetah

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{TITRE}}</title>
<meta name="description" content="{{META_DESCRIPTION}}" />
<!-- Généré automatiquement par le Publiciste Agent — NE PAS éditer à la main.
Source : faisabilités canoniques data_room/PXX/ (contrainte CLAUDE.md #6). -->
<style>
:root {
--bg: {{COLOR_BG}};
--accent: {{COLOR_ACCENT}};
--ink: {{COLOR_INK}};
--muted: {{COLOR_MUTED}};
--font-display: "{{FONT_DISPLAY}}", Georgia, serif;
--font-body: "{{FONT_BODY}}", Georgia, serif;
}
* { box-sizing: border-box; }
body {
margin: 0; background: var(--bg); color: var(--ink);
font-family: var(--font-body); font-size: 18px; line-height: 1.6;
}
header.hero {
padding: 96px 24px 64px; text-align: center;
border-bottom: 1px solid rgba(240,180,41,0.25);
}
header.hero h1 {
font-family: var(--font-display); font-weight: 600;
font-size: clamp(2.4rem, 6vw, 4rem); margin: 0 0 12px; color: var(--ink);
}
header.hero .accent { color: var(--accent); }
header.hero p.tagline { color: var(--muted); max-width: 640px; margin: 0 auto; }
main { max-width: 1120px; margin: 0 auto; padding: 48px 24px 96px; }
.grille { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 28px; }
article.projet {
background: #12121c; border: 1px solid rgba(240,180,41,0.18);
border-radius: 14px; overflow: hidden; display: flex; flex-direction: column;
}
article.projet .media {
aspect-ratio: 16/10; background: #0f0f18 center/cover no-repeat;
border-bottom: 1px solid rgba(240,180,41,0.12);
}
article.projet .corps { padding: 22px 24px 26px; flex: 1; display: flex; flex-direction: column; }
article.projet h2 {
font-family: var(--font-display); font-weight: 600; font-size: 1.6rem;
margin: 0 0 4px; color: var(--ink);
}
.localisation { color: var(--muted); font-size: 0.95rem; margin: 0 0 14px; }
.statut {
display: inline-block; font-family: var(--font-body); font-size: 0.8rem;
letter-spacing: 0.06em; text-transform: uppercase; padding: 4px 12px;
border-radius: 999px; margin-bottom: 14px;
}
.statut.disponible { color: var(--bg); background: var(--accent); }
.statut.en_developpement, .statut.bientot, .statut.en_processus {
color: var(--accent); border: 1px solid var(--accent);
}
.positionnement { color: var(--ink); opacity: 0.92; margin: 0 0 18px; }
table.prix { width: 100%; border-collapse: collapse; margin: 8px 0 6px; font-size: 0.98rem; }
table.prix th, table.prix td { text-align: left; padding: 7px 8px; border-bottom: 1px solid rgba(240,180,41,0.12); }
table.prix th { color: var(--accent); font-family: var(--font-display); font-weight: 600; }
.prix-depuis { color: var(--accent); font-weight: 600; white-space: nowrap; }
.a-venir { color: var(--muted); font-style: italic; margin: 6px 0 10px; }
ul.inclus { list-style: none; padding: 0; margin: 14px 0 0; }
ul.inclus li { padding: 3px 0 3px 20px; position: relative; color: var(--ink); opacity: 0.88; font-size: 0.96rem; }
ul.inclus li::before { content: "◆"; color: var(--accent); position: absolute; left: 0; font-size: 0.7rem; top: 6px; }
footer.legal { color: var(--muted); font-size: 0.82rem; text-align: center; padding: 32px 24px 56px; border-top: 1px solid rgba(240,180,41,0.15); }
</style>
</head>
<body>
<header class="hero">
<h1>Helios <span class="accent">RD</span></h1>
<p class="tagline">{{TAGLINE}}</p>
</header>
<main>
<div class="grille">
{{PROJETS}}
</div>
</main>
<footer class="legal">
{{FOOTER}}
</footer>
</body>
</html>