frontend/portails/out_fresh/ (workspace.json + MANIFEST.json) était tracké mais
commité par accident une seule fois (f954cc1, 2026-08-02, scratch « fresh rebuild ») :
byte-identique à out/, produit par RIEN (le générateur n'écrit que dans out/ via
_DEFAULT_OUT), référencé NULLE PART (git grep out_fresh = 0). Il échappait à
check_artifacts (find -type d -name out = match EXACT sur `out`, jamais `out_fresh`)
donc au balayage reverse-bijection → bombe de staleness silencieuse (rien ne le
régénère : dès que la source bouge, out/ se met à jour et out_fresh/ diverge en copie
stale non-possédée). Suppression (out/ intacte, régénère byte-identique). Occurrence
isolée → pas de gate (#5). Candidat is_filled() écarté au byte-level (non-défaut, #6).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Livre le dernier volet ouvert de Sprint 4 : Frontend Console « 5 portails
(Ventes/Construction/Achat/Compta/Direction) ». Contrainte #1 (ERPNext natif) :
le portail de landing par rôle EST le DocType Workspace v15 → 5 Workspaces natifs.
Anti-invention (#6) : rôles et DocTypes dérivés du contrat rbac_50_roles.json ;
chaque lien/raccourci vise un DocType présent dans les permissions_cibles du
portail (droit prouvé) ; couverture exhaustive ; flag custom issu du contrat ;
tokens de marque repris verbatim de CLAUDE.md #4. CLI + 12 invariants + 19 tests
(4 négatifs). Régression 260 tests verts. Hand-off VPS #8 documenté.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>