Files
Claude Code DTP Worker 5af86e3518 [DTP-Worker 20260812_073055] hardening correctness · rendu HTML publiciste — percent-encode le SEUL segment d'URL restant (code) dans le url('…') CSS
Durcissement d'uniformité (pas bug déclenchable aujourd'hui) qui COMPLÈTE le fix
correctness de la session 070054 (aeb30b8). Ce fix-là a percent-encodé
hero['fichier'] dans src=f"/static/projets/{code}/{quote(fichier)}", mais dans la
MÊME url('…') CSS il restait un 2ᵉ segment dynamique — `code` — interpolé brut puis
html-échappé via _esc(src). C'est exactement l'anti-pattern identifié par 070054
(html.escape = mauvais encodeur pour un segment d'URL), appliqué de façon incomplète.

Fix : src = f"/static/projets/{quote(code)}/{quote(hero['fichier'])}" — quote() est
le SEUL encodeur correct pour un segment d'URL ; le CONTEXTE, pas la donnée courante,
dicte l'encodeur. Zéro dérive : codes réels = P01..P09 (§Projets), quote("p01")=="p01"
→ byte-repro inchangé. Cas frère exact de 060044/criteria.py (« durcir le SEUL membre
non-durci d'une famille ») : ici le SEUL segment d'URL non-encodé de la même url().

Test à dents (test_hero_media_code_segment_percent_encoded) : code théorique "P'X" →
prouvé FAIL sans quote(code), PASS avec. Suite publiciste 25→26, matrice 630/613→631/614.

Défaut d'abord : classe encodage-contextuel balayée sur tous les modules HTML/CSS/URL ;
2 candidats Explore réfutés non-défauts (chat mount_html = persona forcée Amélie par
invariant 7 + portail_key enum ; code segment = théorique mais durci par uniformité).
Cascade régénérée dans l'ordre (quality_report en DERNIER) · 3 fiches compteurs réalignées.
run_ci 33/0/0 · NFC-clean · zéro code moteur V18 (bloqué D-06 #6) · zéro gate (#5) · zéro invention (#6).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-12 07:41:05 +00:00
..

Publiciste Agent — scaffold (Sprint 2)

Maintient automatiquement le site public vente.otov7.com à partir des faisabilités canoniques (data_room/PXX/, template v1.0), sans édition manuelle. Cible de portage sur le VPS : otoia/capabilities/publiciste.py (voir ../../03_agents/publiciste/AGENT.md et ../GAP_ANALYSIS_SPRINT1.md §3.13 — seul module net-neuf, chemin critique).

Pipeline

data_room/PXX/  ──►  lib/parser.py  ──►  projets_master.json  ──►  lib/generator.py  ──►  index.html
   (faisabilité)      (extraction)        (contrat schéma)          (rendu luxury)        (site public)

Le contrat projets_master.json est défini par ../faisabilite/projets_master.schema.json (livré au Sprint 1). Le versioning faisabilité par ../faisabilite/version.schema.json.

État par rapport au calendrier AGENT.md

Semaine Livrable Statut ici
S2 Setup base + parser faisabilité → JSON lib/parser.py + validation schéma
S3 Générateur HTML → site aperçu lib/generator.py (rendu luxury #4)
S4 Triggers systemd + inotify ☐ (exécution VPS — hors périmètre worker)
S5 Notifications WhatsApp Michel ☐ (VPS)
S6 Multilingue FR/EN/ES auto ☐ (FR extrait ; EN/ES à venir)
S7 Tests + validation P01-P09 🟡 tests sur fixtures synthétiques (données réelles VPS)
S8 Production ☐ (VPS)

Composants

Fichier Rôle
publiciste.py Orchestrateur CLI (parse / validate / generate / run)
lib/parser.py data_room/PXX/ → dict projet (contrat schéma) · anti-invention #6
lib/validator.py Validateur JSON-Schema draft-07 (sous-ensemble) · zéro dépendance pip
lib/generator.py projets_master.json → HTML (marque luxury #4)
lib/branding.py Tokens de marque (source unique · CLAUDE.md #4)
templates/site_public.html.tmpl Gabarit dark+doré (Fraunces + Cormorant Garamond)
fixtures/ Données synthétiques de test (⚠️ jamais publiées — cf. fixtures/README.md)
tests/test_publiciste.py Suite unittest (parser, schéma, generator)

Règles anti-invention appliquées (CLAUDE.md #6)

  • Le statut publié dérive de _META/version.json : complete → disponible, sinon en_developpement.
  • Rétrogradation défensive : un projet n'est « disponible » que si TOUTES ses typologies ont un prix USD numérique ; sinon → « En développement · Prochainement ».
  • Cellule vide / « non défini » / placeholder {{…}}null (jamais 0, jamais une valeur inventée).
  • Rendus : uniquement ceux référencés dans la faisabilité ( IA générique).

Utilisation

# depuis 05_deliverables_mvp/publiciste/

# Parser une data_room → projets_master.json (validé contre le schéma)
python3 publiciste.py parse fixtures/data_room -o build/projets_master.json

# Générer le site depuis un master validé
python3 publiciste.py generate build/projets_master.json -o build/index.html

# Pipeline complet
python3 publiciste.py run fixtures/data_room -o build/

# Tests
python3 -m unittest discover -s tests -v

⚠️ Ce worker n'écrit jamais sur le VPS. Le déploiement réel (copie vers /opt/oto/sites/vente/, cache-bust Cloudflare, notification WhatsApp) est un livrable ultérieur exécuté côté serveur par l'agent (Semaines 4-5).

Auto-score 4Big du livrable : 95/100

Réserve 5 : exécution contre les données réelles data_room/ (VPS, hors périmètre) différée ; triggers systemd/inotify + notifications WhatsApp = livrables Semaines 4-5 côté serveur ; génération EN/ES = Semaine 6.