# libreDecision v2 — Blueprint définitif

_Généré par la ronde de conception ultracode du 2026-08-11 : panel de 4 visions + 3 juges, corpus doctrinal (« Mon nom est personne », « Une économie du don », SejeteralO), 4 critiques d'intégration, architecte final, 3 vérificateurs adversariaux, réparations intégrées._

## Manifeste

Personne n'a jamais vécu en démocratie — alors nous la rêvons de jour, et nous la commençons à notre échelle. libreDecision attrape chaque décision en une phrase et propose le chemin le plus court : décide seul et trace-le, décide sous mandat, écoute puis décide, ou décidez ensemble — jamais plus lourd que l'enjeu. Ici on ne délègue pas, on mandate, et on reprend ; on n'est pas consulté, on s'instruit, puis on se prononce ; on ne s'affronte pas en deux camps, on règle des curseurs — et la position de chacun reste visible dans le faisceau, la médiane n'écrase personne, la dernière main est toujours humaine. Les concernés en premier lieu ont voix par construction, chacun voit pourquoi ; lever la main « ça me concerne » est un droit, pas une demande. Chaque décision dit ce qu'elle engage, ce qu'elle cherche à changer, et repasse l'épreuve du réel : rien n'est définitif, toute décision en vigueur garde sa poignée, se tromper est une donnée qu'on consigne. Les règles du jeu elles-mêmes — le Pacte — s'écrivent au fil de l'eau, par ceux qu'elles gouvernent, sous les boussoles Autonomie, Équilibre, Liaison. Du trio d'amis à la toile Ğ1 : le même geste, dix secondes, et l'observatoire qui apprend de nous. La ville change, le puits demeure.

## Delta vs blueprint v1 (chaque changement justifié par la doctrine)

### Δ1

**Changement.** CollectiveSettings.protocolByRange.inertialBinary renommé 'large' (Id résolu par le Pacte, le nom ne présume plus la méthode). Gabarits blank/informal/association/cooperative/commune/symmetric : 'large' pointe le protocole nuancé 6 niveaux (nuancedMinParticipants/nuancedThresholdPct existants). SEULS free-currency et les seeds Ğ1 pointent le binaire inertiel. R5 >50 hors clause : défaut nuancé ; R3 (clause visée) garde clauseByInertia — héritage Ğ1 (33+59 clauses, D30M50B.1G.2) intact, à sa place.

**Raison doctrinale.** « Le clivage binaire est un fléau contre la réflexion, un ennemi fatal : il la tue » ; « proscrire le binaire oui/non comme format par défaut » ; le binaire n'est légitime que « remis à sa place dans la boîte à outils » — jamais destination naturelle des grands corpus (le RIC disqualifié : « tir à la corde »).

### Δ2

**Changement.** VoteMethod += 'parametric' (UI « Réglage collectif / décider au curseur »). Decision.paramSpec { params ≤7, constraint 'none'|'sum100', impactAttrKey? } ; ParamDef { key, label métier obligatoire, kind 'slider'|'share', min/max/step, unit?, baseline?, derived? } ; Vote.values?: number[] ; engine/parametric.ts (validateVote, resolveDerived, medianByElement, computeMyImpact, detectBimodality, crystallize). SPECS VERROUILLÉES (réparations) : médiane BASSE par élément (nombre pair ⇒ élément central inférieur — chaque valeur médiane est une valeur effectivement votée, donc conforme au step par construction, testée vitest) ; sum100 ⇒ EXACTEMENT une part 'derived' exigée à la création du paramSpec ; au vote, la géométrie clampe dynamiquement les parts votées pour que le derived reste dans ses bornes ET validateVote rejette sinon ; à la cristallisation, le derived n'est jamais agrégé : il est résolu depuis la médiane des parts votées, et s'il sort de ses bornes ⇒ clamp à la borne + renormalisation proportionnelle des parts non-derived (somme 100 restaurée, spec testée). Dégénérescences : 0 vote ⇒ baseline, jamais d'écran vide. Jamais dans le chemin 10 s : alternative « C'est un réglage » + suggérée quand la phrase contient nombre/%/montant.

**Raison doctrinale.** « Raisonner curseur / table de mixage » est LE geste central des deux livres ; leçons SejeteralO « voter = régler une forme, pas choisir un camp », « médiane élément par élément » avec l'invariant « le résultat est lui-même une position que chacun aurait pu proposer » — invariant qui EXIGE la médiane basse et la résolution sous contrainte ; « robustesse numérique de série : dégénérescences spécifiées ».

### Δ3

**Changement.** RÉPARATION — Cristallisation = geste humain daté : la clôture paramétrique passe par VoteSession.status 'frozen' à closesAt (votes figés, médiane figée affichée) puis le GARANT (stewardIds, à défaut l'auteur) fait le geste « Cristalliser » → 'closed' + crystallizedById/crystallizedAt, la médiane devient position adoptée (ou rejected si quorum parametricMinParticipants non atteint — constaté au même geste). Relance au Fil du garant s'il tarde. Si detectBimodality positif, le geste affiche en évidence « Deux positions distinctes — cristalliser quand même / scinder / reformuler » : jamais de cristallisation automatique. Consent/nuancé/binaire gardent la clôture automatique (le moteur ne fait qu'y constater un seuil sur un contenu formulé par des humains) ; l'élection clôt automatiquement SAUF égalité (geste requis, cf. delta élection).

**Raison doctrinale.** Tension du corpus explicitement tranchée : « l'agrégat éclaire une délibération, la cristallisation reste un geste humain distinct et daté » ; chez SejeteralO la publication est distincte de l'agrégation vivante ; un calcul qui produit un contenu adopté sans geste humain = « décision algorithmique », repoussoir absolu.

### Δ4

**Changement.** Matière : Decision.brief { context?, sources?, symptomsVsCauses?, effects: Effect[] } avec Effect { label, target?, measured? }. RÉPARATION — garde restreinte : ≥1 effet recherché exigé (state.ts, framing→voting / draft→voting) UNIQUEMENT pour les sessions collectives de poids binding/structural, gouverné par la clause de Pacte 'triage.requireEffects' ∈ 'none'|'structural'|'binding' (défaut 'binding' ; 'none' seedé dans blank ET informal) ; quand elle s'applique au structural, ≥1 effet doit porter une cible mesurable (target). Le consentement léger (weight light) et le trio n'y sont JAMAIS soumis ; le tunnel ne l'affiche jamais — la matière s'écrit en framing. L'épreuve du réel réutilise effects : tableau « recherché / cible / constaté » au-dessus des 3 boutons, mesures saisies par les measurerIds.

**Raison doctrinale.** « Il n'est pas consulté, il est instruit » ; « aucun vote sans matière » — mais « jamais plus lourd que l'enjeu » (manifeste) : le collectif règle sa propre exigence (couche méta), et la boucle « acquisition collective des expériences » est fermée par le même champ.

### Δ5

**Changement.** RÉPARATION — Bloc « S'instruire » condensé : en salle de vote et fiche (framing/voting), un RÉSUMÉ COMPACT toujours visible avant les boutons (titre, effets recherchés, « Ce que ça engage ») avec versions+diff, avis/objections et provenance en DÉPLIABLES internes ; le bloc entier se replie automatiquement aux visites suivantes de la même personne (préférence locale par décision) — voter sur un titre nu reste impossible, re-voter reste léger.

**Raison doctrinale.** « Aucun vote sans matière » concilié avec la leçon SejeteralO « agir d'abord, comprendre ensuite » : pas de mur de scroll mobile à chaque re-vote d'un vote révisable.

### Δ6

**Changement.** Ressources : Decision.resources { note, amount?, unit 'heures'|'€'|'DU'|string } + Decision.measurerIds à côté de stewardIds. Huile/eau : DU et € JAMAIS convertis ni additionnés (Observatoire : lignes par unité). RÉPARATION — localisation de la garde : la ligne « Ce que ça engage » est SUGGÉRÉE-dépliée dans le tunnel (toujours facultative, ne bloque jamais) ; l'exigence (weight != 'light' hors solo) vit UNIQUEMENT dans la garde d'ouverture de fenêtre/session de state.ts, même mécanique que brief.effects — le tunnel n'affiche jamais un champ obligatoire qui ne change pas le chemin.

**Raison doctrinale.** « Chaque décision implique une allocation de ressources, donc une décision connexe incontournable » (MNP p.89) ; « des équipes pour réaliser et des équipes pour mesurer » ; l'huile et l'eau ne se mélangent jamais ; et la métrique produit « 2 gestes / 10 s » reste intacte pour le cas nominal.

### Δ7

**Changement.** Suppression de toute jauge de confiance sur les mandats. Bloc « Exercice du mandat » : faits comptés jamais synthétisés (« 12 décisions tracées · 1 objection · 2/2 rapports », listes cliquables). Le flux public devient « Feux de la rampe » (« toutes les décisions prises sous ce mandat — contestables »). Rendu seulement, zéro champ.

**Raison doctrinale.** « On peut évaluer des choses ; mais les personnes ? Alerte rouge » ; « la confiance exclut le contrôle » — une jauge de CONFIANCE sur un pouvoir contrôlé est un oxymore ; « Feux de la rampe » est le mot doctrinal exact.

### Δ8

**Changement.** Protocol.ballot: 'open' | 'secret' (défaut 'open'), gouverné par le Pacte. Mode secret v2 : décomptes agrégés et commentaires anonymisés seuls, étiquette « secret d'affichage — démo » ; crypto réelle avec la synchro (v3, noté au contrat des stores).

**Raison doctrinale.** « Modalité de vote secret vs ouvert comme paramètre de chaque décision » (leçon fouloscopie) — « l'outil doit savoir faire les deux, et le périmètre en décide » ; même honnêteté d'étiquetage que la gravure.

### Δ9

**Changement.** RÉPARATION — Historique des re-votes jamais opposable : la chaîne supersedesVoteId n'est JAMAIS affichée publiquement sous le nom d'un votant — visible du seul auteur du vote ; même en ballot 'open', seul le DERNIER vote actif d'une personne apparaît sous son nom, et le décompte agrégé est recalculé. Libellé : « Ton vote est remplaçable — ton historique n'est visible que de toi. » Invariant écrit au modèle + test.

**Raison doctrinale.** Tension « Mémoire vive vs droit à l'erreur » tranchée par le corpus : « archiver les mandats, pardonner les positions ; l'historique des votes révisés n'est jamais opposable à une personne » — sinon contrôle des personnes entre elles, le glas doctrinal.

### Δ10

**Changement.** Gabarit 'blank' « Page blanche — observatoire d'abord » en tête de /creer : Pacte d'une seule clause (« nous consignons nos décisions ; le reste s'écrira au fil de l'eau ») + clause A1 finalité. RÉPARATION — cohérence settings : blank seede AU MINIMUM le protocole « Consentement » ; protocolByRange.consent est OBLIGATOIRE (invariant : tout collectif a un protocole de consentement, validé à l'import), les autres clés optionnelles ; resolveSettings spécifie le REPLI : toute clé non résolue retombe sur consent ; si consent introuvable (bundle corrompu), le triage route 'advice' avec bannière « Aucun protocole — crée-le ou décide sur avis » — jamais un crash. Les 5 gabarits existants réétiquetés « point de départ — tout est amendable par décision ».

**Raison doctrinale.** « L'observatoire des décisions AVANT de protocoliser » ; contrat social « écrit au fil de l'eau, sans préfigurer » ; « une chorégraphie du don ne se télécharge pas en .ods » — et la robustesse spécifiée (« jamais d'écran vide ») fait partie de la leçon SejeteralO.

### Δ11

**Changement.** Gabarit 'symmetric' « Institution symétrique » : Pacte en transparence totale, TextDoc reference « Déclaration de symétrie », première décision « Déclarer notre symétrie », 2 mandats suggérés Ambassadeur et Négociateur. Données pures templates.ts.

**Raison doctrinale.** « Tu ne veux pas — Ô Institution — tant pis, je crée une symétrie » (chapitre entier de MNP) ; le poids = le volume des personnes qui l'habitent ; représentants « ambassadeurs et négociateurs, jamais décideurs ».

### Δ12

**Changement.** DecisionRoute += 'record' + Decision.decidedHow. Alternative permanente « Déjà tranché — je le consigne » → adopted immédiat, zéro fenêtre (2 gestes). /decisions/memoire → /decisions/observatoire « L'Observatoire » ; ratio consigné/outillé = maturité ; maturation (famille R6, jamais bloquante) : ≥3 consignations partageant ≥2 tags ⇒ « Cette pratique est mûre — protocolise-la en quelques phrases » pré-remplissant une clause de Pacte.

**Raison doctrinale.** « Consigner comment elles se prennent dans les faits, avant de protocoliser » puis « traduire en protocoles de quelques phrases quand c'est mûr » — le flux consigner→observer→protocoliser devient le chemin ordinaire ; l'inversion des noms (Mémoire/Observatoire) est corrigée.

### Δ13

**Changement.** chainKind += 'element' + Concern.priority?: 0|1|2|3. « Découper en micro-décisions » sur toute décision structural en framing ; onglet « Éléments » + « Cartographie de clôture » auto-générée. RÉPARATION — clôture du dossier : nouvelle transition framing→closed du parent, gardée par « tous les éléments dans un état terminal » ; jamais automatique : quand la garde est vraie, carte au Fil du garant « Le dossier est complet — clore et publier la cartographie de clôture » ; le geste clôt, la cartographie devient le PV du dossier. Libellé UI : « décision incrémentielle » réservé à CE mode.

**Raison doctrinale.** La « décision incrémentielle » est le nom PROPOSÉ PAR L'AUTEUR (modèle Taïwan) : « chacun navigue dans les points qui le concernent, pondère ses préférences » ; « cartographie de clôture visible de tous » — et la clôture est un geste humain, cohérent avec la cristallisation.

### Δ14

**Changement.** Decision.baselineNote?: string (« Aujourd'hui : … ») suggéré routes advice/collective (jamais solo). Fiche : « Aujourd'hui → Proposé » ; ParamDef.baseline = marqueur « actuel » ; l'épreuve du réel relit l'écart. Les clauses gardent leur diff (qui EST leur baseline).

**Raison doctrinale.** Leçon SejeteralO : « la baseline : toujours afficher l'existant — une décision se lit comme un écart au statu quo, mesurable ».

### Δ15

**Changement.** Person.attributes?: Record<string, number> (déclarés, édités dans /donnees, jamais affichés à autrui ni agrégés) + ParamSpec.impactAttrKey. RÉPARATION — computeMyImpact 'linear-share' SPÉCIFIÉ : ne s'applique QUE si constraint='sum100' ET resources.amount présent ET impactAttrKey présent ; pour chaque part p (derived inclus) : montant(p) = resources.amount × p/100 ; ma quote-part(p) = montant(p) × attr(moi) / Σ attr(déclarants du corpus) ; résultat par curseur + total, calculé aux trois positions (la mienne / médiane / baseline). attr(moi) absent ou Σ=0 ⇒ null (jamais un chiffre inventé — la carte montre alors seulement l'écart collectif en valeurs de curseurs). L'exemple seedé « budget d'ateliers 1 200 € × 'heures/mois' » devient calculable. Étiquette « estimation sur données déclarées ».

**Raison doctrinale.** Principe produit n°5 « qu'est-ce que ça change pour moi » ; SejeteralO « Mon prix au m³ » et son triptyque ma-position/médiane/référence — version local-first honnête, bornée, et désormais calculable ; le moteur sur données réelles importées reste v3.

### Δ16

**Changement.** Composant Faisceau : nuancé → histogramme des 6 niveaux sous la jauge + commentaires en liste — jamais un simple adopté/rejeté ; paramétrique → strip-plot par curseur (points translucides anonymes, médiane trait épais, baseline marqueur creux). detectBimodality ⇒ bannière non bloquante « Deux positions distinctes se dessinent sur X — scinder la question ou changer de référentiel ? » ouvrant le découpage ou une reformulation.

**Raison doctrinale.** « La diversité reste visible : le faisceau » ; « la médiane peut masquer une distribution bimodale » ; le « changement de référentiel » doctrinal devient un geste produit.

### Δ17

**Changement.** Garde d'affluence dans state.ts : concernEscalateRatio atteint ⇒ la fenêtre/session NE PEUT PAS se clore sans traitement — élargir d'un cran (1 tap, re-triage journalisé) ou maintenir avec Decision.scopeKeptNote motivée publique, affichée en évidence.

**Raison doctrinale.** La montée d'échelle est DÉCIDÉE (« on en change quand le peuple le décide ») — l'asymétrie de dérogation appliquée au périmètre, LE lieu doctrinal de la subsidiarité.

### Δ18

**Changement.** Circle.kind?: 'place'|'theme'|'team' — iconographie, groupement, ordre des suggestions seulement, jamais un droit. Hint non bloquant si 'team' > 12. Gabarits typent leurs cercles.

**Raison doctrinale.** Les 3 vecteurs du périmètre : le topologique gagne une existence déclarative sans machinerie géo ; « au-delà de douze, les complications commencent ».

### Δ19

**Changement.** Collective.lineage?: { sourceSlug, exportedAt, sha256 } posé à l'import d'un bundle étranger (« essaimé de … »). PAS de parentCollectiveId ; pattern v3 documenté : une grappe = un collectif ordinaire dont les membres sont des mandataires d'autres collectifs.

**Raison doctrinale.** La « scission féconde » exige la mémoire d'origine ; le corpus proscrit la fédération dans le modèle (« représentation, voix unique ») — composition pure.

### Δ20

**Changement.** Clause « A1 — Notre finalité » seedée dans chaque gabarit (texte sous vote permanent, défaut portant les boussoles), affichée sous le nom du collectif ; préambule de Pacte proposé et modifiable portant Autonomie–Équilibre–Liaison.

**Raison doctrinale.** « Chaque groupe doit se donner une finalité » ; « boussoles proposées, jamais imposées » ; coexistence « sans être concurrents ».

### Δ21

**Changement.** Passerelles hors-écran : (1) CSS print A4 + « Imprimer » sur fiche (PV) et salle (affiche de session) ; (2) « agir en tant que » requalifié « mode atelier » via recordedById?: Id sur Vote/Advice/Objection/Assent (« saisi par X pour Y — atelier du <date> ») ; (3) micro-copy « à trancher en présence ? » sur structural.

**Raison doctrinale.** « Toute décision numérique doit être praticable sans écran » ; « intermédiation humaine pour les démunis du numérique » ; « plus le numérique prend de place, plus il faut en donner à la relation humaine ».

### Δ22

**Changement.** frontend/app/lexicon.ts : source UNIQUE des libellés français + test vitest anti-lexique (déléguer/délégation, dette, retard, en souffrance, corpus, snapshot, CONTRE, verdict, triage, oligarchie, peuple adulte…). Trois règles de ton : (1) tutoyer la personne dans ses gestes ; (2) actes collectifs à l'infinitif (« Adopter notre Pacte ») ; (3) sujet grammatical des phrases générées = le collectif ou la légitimité, jamais la formule/le système.

**Raison doctrinale.** « L'auteur voudra retrouver ses mots exacts » ; « politiquement explicable » : c'est la légitimité qui exige, pas les maths — l'algorithme au service, jamais souverain.

### Δ23

**Changement.** Renommages UI (types inchangés) : carte /decider « Le chemin » (explanation seule, code R* au dépliable/PV/journal) ; « Concerné·e en premier lieu / en second lieu » (pastille pleine / main levée) ; chip « Concernés » ; panneau « Périmètre » ; « Mandats — les pouvoirs confiés » ; Fil « À toi de décider » compteur nu ; « Qui vote : 11 personnes — liste arrêtée le 12/08 » (W/T/M/B/G/C à l'Atelier seulement) ; échelle nuancée « Pas du tout · Plutôt non · Réservé · Plutôt oui · Oui · Tout à fait » ; revoyure « L'épreuve du réel » / « Le réel a-t-il suivi ? » / « Ça tient / À revoir / À révoquer » ; « C'est urgent » + badge « Décidé en urgence — le collectif ratifie » ; « Révoquées — ce qu'on en a appris » ; routes à la première personne ; sous-titre du Pacte « Notre contrat social — sacralisé, jamais immuable » ; fenêtre framing « N jours pour s'instruire et formuler des contre-propositions ».

**Raison doctrinale.** « On ne délègue pas, on abandonne » ; « périmètre » est LE concept pivot ; le registre dette/peur est le couple de l'asservissement ; « le verdict n'est jamais un juge » ; « toute étiquette évacue la moitié de la population ».

### Δ24

**Changement.** Bouton « Explorer » en salle paramétrique : sandbox de la médiane, curseurs libres, dérivés + « Pour moi » recalculés en live, « Partir de la médiane / du statu quo / de ma position », « En faire ma position », étiquette « exploration — ne compte pas ». Rétro-manipulation résultat→contrainte : v3.

**Raison doctrinale.** Leçon SejeteralO « manipulation de l'agrégat pour explorer » ; « déplacer, évaluer, ajuster, recommencer » ; abaisse le coût psychologique du premier vote.

### Δ25

**Changement.** Dépersonnalisation d'affichage : contenu et diff d'abord, auteur replié (visible au tap) dans le bloc formulation et la salle. Pas de mode anonyme réel en v2 mono-poste.

**Raison doctrinale.** « On débat du contenu, pas de l'auteur » (@personne) — anti ad-hominem, anti-sauveur.

### Δ26

**Changement.** Désirabilité : tampon 井 incliné -10° « Décidé » (translateY 0.12s) + « C'est décidé — et révisable, comme tout ici » ; /creer se signe « Osons, nous verrons » ; manifeste public sans vocabulaire de livraison, clos sur « La ville change, le puits demeure » ; termes-diagnostics réservés à la page-manifeste.

**Raison doctrinale.** « Rendre désirable plutôt que convaincre » exige que décider soit beau ; le couple plaisir+rêve.

### Δ27

**Changement.** RÉPARATION — Silence-vaut-accord résolu par l'entité Assent : « Ça me va » est stocké (Assent { decisionId, personId, recordedById? }, append-only, dans le Bundle). Règle unique des fenêtres d'objection : à échéance sans objection ouverte — reversibility 'easy' ⇒ adopted (silence vaut accord) ; costly/irreversible ⇒ adopted SEULEMENT si ≥1 Assent d'un concerné autre que l'auteur, sinon la fenêtre se PROLONGE d'un cran (windowEndsAt + objectionWindowHours, relance au Fil des concernés) — jamais d'adoption par pur silence hors easy. La contradiction v1 est levée, le comportement à échéance est défini.

**Raison doctrinale.** « Il ne cède pas un consentement, il négocie — chaque virage » : hors du réversible, l'accord est un geste, pas une absence ; et le silence-vaut-accord réservé au réversible était déjà l'engagement du blueprint — désormais tenu mécaniquement.

### Δ28

**Changement.** RÉPARATION — Élection codable : Vote.choicePersonId?: Id (invariant : EXACTEMENT un de value / values / choicePersonId selon la méthode, validé par le store). Vote déposé sans choicePersonId = blanc (compte pour la participation, pas pour la désignation). engine/threshold.ts += electionResult(votes, corpus) : quorum optionnel electionMinParticipants (FormulaParams) sinon rejected ; désignation à la PLURALITÉ simple des derniers votes actifs ; ÉGALITÉ ⇒ résultat 'tie' avec exAequoIds — l'outil ne départage JAMAIS : la clôture propose une session de départage chaînée restreinte aux ex æquo (pré-remplie), ou le tirage au sort entre ex æquo si le protocole du Pacte l'a prévu d'avance. Salle élection : désignation par avatar, bouton « Vote blanc », règle affichée en une phrase.

**Raison doctrinale.** L'élection « remise à sa place dans la boîte à outils » doit être réellement outillée pour les mandats ; « l'outil représente, agrège et éclaire — il ne décide jamais » : une égalité n'est pas tranchée par un algorithme, et le tirage au sort n'est légitime que décidé d'avance par le Pacte.

### Δ29

**Changement.** RÉPARATION — canTransition étendu : engine/state.ts expose canTransition(decision, to, ctx: { concerns: Concern[], settings: CollectiveSettings, session?: VoteSession, children?: Decision[], assents?: Assent[], objections?: Objection[] }) — les gardes d'affluence (|déclarés| vs |calculés|), de matière (requireEffects), de ressources, de frontière (windowSuspendedAt), d'Assent et de clôture de dossier (children terminaux) ont leurs entrées dans la signature ; le contexte est assemblé par le store, la fonction reste pure et testable.

**Raison doctrinale.** Machine à états UNIQUE réellement implémentable : toute mutation passe par canTransition, et les gardes doctrinales (subsidiarité, instruction, consentement) sont du code testé, pas des intentions.

### Δ30

**Changement.** Verdict enrichi : parametricHint (nombre/%/montant détecté), alternatives permanentes « C'est un réglage — décidez au curseur », « Déjà tranché — je le consigne », binaire avec son coût (« deux camps, un perdant ») ; TriageContext.similarRecordedCount ; suggestion.kind += 'protocolize'.

**Raison doctrinale.** « L'outil représente, agrège et éclaire — il ne décide jamais » : les modalités doctrinales sont proposées, jamais imposées.

## Modèle de domaine

```ts
// ─────────────────────────────────────────────────────────────
// libreDecision v2 — noyau de domaine DÉFINITIF (frontend/app/types/domain.ts)
// Local-first, prêt-synchro. Identifiants anglais, UI française via app/lexicon.ts.
// INVARIANT 1 : LA DÉCISION EST L'OBJET PIVOT — mandats, clauses, révocations,
//   ratifications, éléments de dossier en sont des spécialisations chaînées.
// INVARIANT 2 : l'unification est un fait d'architecture, JAMAIS un vocabulaire d'UI.
// INVARIANT 3 : aucun agrégat/score sur une personne, nulle part — on évalue des choses.
// INVARIANT 4 : aucun calcul ne produit un contenu adopté sans geste humain daté
//   (cristallisation paramétrique, départage d'élection, clôture de dossier).
// ─────────────────────────────────────────────────────────────

type Id = string        // UUIDv7 — triable par temps, prêt pour la synchro
type ISODate = string   // ISO 8601 UTC
type Json = string | number | boolean | null | Json[] | { [k: string]: Json }

// Toute entité : scoping collectif + horloge de synchro + soft-delete.
// Prêt-synchro : jamais de suppression physique (archivedAt) ;
// Vote/Advice/Objection/Assent append-only (merge trivial) ; le reste LWW par updatedAt.
interface Entity {
  id: Id
  collectiveId: Id
  createdAt: ISODate
  updatedAt: ISODate
  archivedAt?: ISODate
}

// ── Le tenant ────────────────────────────────────────────────
type CollectiveTemplate =
  | 'blank'          // « Page blanche — observatoire d'abord » : Pacte 1 clause + A1, protocole Consentement SEUL
  | 'informal' | 'association' | 'cooperative' | 'commune'
  | 'free-currency'  // héritage Ğ1 — SEUL gabarit où protocolByRange.large = binaire inertiel
  | 'symmetric'      // « Institution symétrique » — le geste fondateur militant (MNP)

interface Collective {
  id: Id
  slug: string
  name: string
  color: string                    // hex accent
  icon: string                     // i-lucide-*
  template: CollectiveTemplate
  isTransparent: boolean
  pactDocId: Id                    // TextDoc role='pact' — les réglages EN document voté
  rootCircleId: Id
  lineage?: {                      // essaimage : posé auto à l'import d'un bundle d'un autre collectif
    sourceSlug: string; exportedAt: ISODate; sha256: string
  }                                // PAS de parentCollectiveId — la fédération n'est jamais une table
  createdAt: ISODate
  updatedAt: ISODate
}

// ── Les gens ─────────────────────────────────────────────────
interface Person extends Entity {
  displayName: string
  isMe: boolean                    // profil local (v2 mono-poste ; « mode atelier » pour la saisie en présence)
  attributes?: Record<string, number>  // attributs DÉCLARÉS par la personne (clés libres : 'heures/mois'…),
                                       // édités dans /donnees — servent UNIQUEMENT la carte « Pour moi » ;
                                       // jamais affichés à autrui, jamais agrégés (invariant 3)
  duniterAddress?: string          // SS58 — réservé auth Duniter (hors scope v2)
  wotStatus?: 'member' | 'smith'
}

interface Circle extends Entity {
  name: string
  purpose: string
  kind?: 'place' | 'theme' | 'team'  // lieu / thème / équipe — iconographie, groupement des sélecteurs,
                                     // ordre des suggestions SEULEMENT, jamais un droit ;
                                     // hint non bloquant si 'team' > 12 (« au-delà de douze… »)
  memberIds: Id[]                  // v2 : liste nominative = SEULE règle d'appartenance (auditable)
  parentCircleId?: Id              // emboîtement (couches d'oignon) — racine = rootCircle
  domains: string[]                // tags — SUGGESTION/découverte uniquement, jamais un droit de vote
}
// Corpus d'une décision = union CALCULABLE : membres des cercles du scope
// ∪ personnes nommées ∪ titulaires des mandats dont le domaine intersecte.
// Chaque inclusion garde sa raison (Concern.reason) — auditable en un tap.

// ── LA DÉCISION (pivot) ──────────────────────────────────────
type Reversibility = 'easy' | 'costly' | 'irreversible'
type Weight = 'light' | 'binding' | 'structural'

type DecisionRoute =
  | 'solo'        // « Je décide » (trace facultative hors mandat)
  | 'mandate'     // « Je décide, sous mandat » (trace obligatoire + fenêtre d'objection)
  | 'transmit'    // « Je transmets » — le mandat d'un autre couvre
  | 'advice'      // « J'écoute, puis je décide »
  | 'collective'  // « Nous décidons » (modalité du protocole)
  | 'record'      // « Déjà tranché — je le consigne » : l'observatoire en un geste, adopted immédiat

type DecisionStatus =
  | 'draft' | 'advice' | 'objection' | 'framing' | 'voting'
  | 'adopted' | 'rejected' | 'revoked' | 'transmitted' | 'closed'

type TriageRule = 'R-U' | 'R0a' | 'R0b' | 'R0c' | 'R2' | 'R3' | 'R4' | 'R5' | 'R6'

// La matière à réflexion — « il n'est pas consulté, il est instruit »
interface Effect {
  label: string                    // effet recherché, une ligne
  target?: string                  // cible mesurable optionnelle (« ≤ 400 € », « +5 membres »)
  measured?: { note: string; at: ISODate; byId: Id }  // constaté — saisi par les measurerIds à la revoyure
}
interface Brief {
  context?: string
  sources?: { label: string; url: string }[]
  symptomsVsCauses?: string        // qualification doctrinale du sujet
  effects: Effect[]                // GARDE (state.ts, ouverture de session collective UNIQUEMENT) :
                                   // exigé si weight ∈ requireEffects du Pacte ('binding' couvre binding+structural) ;
                                   // structural sous garde ⇒ ≥1 effet avec target. JAMAIS pour weight 'light',
                                   // JAMAIS dans le tunnel, JAMAIS en solo/record.
}

// La décision connexe de ressources — « chaque décision implique une allocation »
interface Resources {
  note: string                     // « Ce que ça engage » en une phrase
  amount?: number
  unit?: 'heures' | '€' | 'DU' | string  // huile/eau : unités JAMAIS converties ni additionnées entre elles
}

// Le réglage collectif — « raisonner curseur / table de mixage »
type ParamKind = 'slider' | 'share'      // 'curve' réservé v3 (SejeteralO complet)
interface ParamDef {
  key: string
  label: string                    // libellé métier OBLIGATOIRE — jamais de a, b, c bruts
  kind: ParamKind
  min: number; max: number; step: number
  unit?: string
  baseline?: number                // statu quo — marqueur creux toujours affiché
  derived?: boolean                // résolu par la contrainte, jamais voté — étiquette « calculé »
}
interface ParamSpec {
  params: ParamDef[]               // ≤ 7 (leçon SejeteralO : petit espace manipulable)
  constraint: 'none' | 'sum100'    // sum100 : les 'share' totalisent 100 ; EXACTEMENT UNE part 'derived'
                                   // exigée (validé à la création du spec) — la variable d'absorption ;
                                   // résolue linéairement (100 − Σ autres)
  impactAttrKey?: string           // clé de Person.attributes → carte « Pour moi » ('linear-share')
}

interface Decision extends Entity {
  authorId: Id
  title: string                    // LA phrase de capture — seul champ obligatoire
  body?: string                    // progressive disclosure
  brief?: Brief                    // matière — cf. garde d'ouverture ci-dessus
  baselineNote?: string            // « Aujourd'hui : … » — suggéré routes advice/collective, jamais solo
  resources?: Resources            // SUGGÉRÉ-déplié au tunnel (jamais bloquant) ; EXIGÉ à la garde
                                   // d'ouverture de fenêtre/session (state.ts) si weight != 'light' hors solo
  tags: string[]                   // découverte + récurrence — JAMAIS un corpus

  // — verdict de triage (pré-rempli par le moteur, ajustable) —
  reversibility: Reversibility
  weight: Weight
  urgent: boolean
  scope: {
    selfOnly: boolean
    circleIds: Id[]                // plancher : élargir libre, JAMAIS restreindre sous le calcul
    personIds: Id[]
  }
  route: DecisionRoute
  triageRule: TriageRule           // relégué au dépliable « pourquoi ? », PV, journal — jamais en carte
  routeOverridden: boolean
  overrideNote?: string            // OBLIGATOIRE si allègement (asymétrie de dérogation)
  scopeKeptNote?: string           // affluence atteinte + périmètre maintenu ⇒ note motivée PUBLIQUE obligatoire

  // — spécialisations chaînées (le pliage génératif) —
  underMandateId?: Id              // décidée SOUS ce mandat → feux de la rampe du mandat
  createsMandate?: MandateDraft
  amendsClauseId?: Id
  protocolId?: Id                  // résolu par le Pacte, ajustable en alourdissant
  paramSpec?: ParamSpec            // si modalité paramétrique (« Réglage collectif »)
  parentDecisionId?: Id
  chainKind?: 'ratification' | 'revision' | 'revocation' | 'element'
                                   // 'element' : micro-décision d'un dossier découpé (« décision incrémentielle »).
                                   // Parent d'éléments : transition framing→closed gardée par
                                   // « tous les enfants element en état terminal » — GESTE du garant
                                   // (carte au Fil « Le dossier est complet — clore »), jamais automatique ;
                                   // la cartographie de clôture devient le PV du dossier.
  decidedHow?: string              // route 'record' : « comment ça s'est décidé », une phrase

  // — temps & fenêtres —
  status: DecisionStatus
  windowEndsAt?: ISODate
  windowSuspendedAt?: ISODate      // objection de frontière → compte à rebours SUSPENDU (visible)
  decidedAt?: ISODate
  review?: Review                  // obligatoire si irreversible || structural
  sunsetAt?: ISODate
  stewardIds: Id[]                 // garants — RÉALISENT (et cristallisent le paramétrique, clôturent le dossier)
  measurerIds: Id[]                // mesureurs — CONSTATENT (saisissent Effect.measured) : la paire doctrinale

  // — preuve & visibilité —
  visibility: 'private' | 'scope' | 'collective'   // anti-flicage : trace maîtrisée hors mandat
  engraving?: Engraving
}

interface Review {
  dueAt: ISODate
  verdict?: 'confirmed' | 'revise' | 'revoke'   // UI : « Ça tient / À revoir / À révoquer »
  note?: string
  decidedAt?: ISODate
}

interface Engraving {
  sha256: string
  engravedAt: ISODate
  proofLevel: 'local'              // v2 : UNIQUEMENT 'local' — « empreinte locale — démo »
  ipfsCid?: string                 // réservé v3 — jamais affiché tant que non réel
  chainRef?: string
}

// ── Inclusion & fenêtres ─────────────────────────────────────
interface Concern extends Entity {
  decisionId: Id
  personId: Id
  origin: 'computed' | 'declared'  // UI : « concerné·e en premier lieu » / « en second lieu »
  reason: string                   // « membre du cercle Forgerons » — affichée au tap
  declaredNote?: string
  beforeSnapshot: boolean          // avant liste arrêtée ⇒ vote ; après ⇒ voix consultative
  priority?: 0 | 1 | 2 | 3         // « pondère tes enjeux » — par élément d'un dossier ;
                                   // nourrit la cartographie de clôture, jamais le droit de vote
}

interface Assent extends Entity {  // « Ça me va » STOCKÉ — l'accord explicite des fenêtres d'objection
  decisionId: Id
  personId: Id
  recordedById?: Id                // mode atelier
}
// RÈGLE DES FENÊTRES D'OBJECTION (state.ts) : à échéance sans objection ouverte —
// easy ⇒ adopted (silence vaut accord) ; costly/irreversible ⇒ adopted SEULEMENT si
// ≥1 Assent d'un concerné ≠ auteur, sinon PROLONGATION d'un cran (+objectionWindowHours,
// relance au Fil) — jamais d'adoption par pur silence hors easy.

interface Objection extends Entity {
  decisionId: Id
  personId: Id
  kind: 'content' | 'boundary'     // boundary SUSPEND la fenêtre — la frontière prime sur le fond
  argument: string
  status: 'open' | 'withdrawn' | 'integrated' | 'escalated'
  resolutionNote?: string
  recordedById?: Id                // mode atelier : « saisi par X pour Y — atelier du <date> »
}

interface Advice extends Entity {
  decisionId: Id
  personId: Id
  position: 'favorable' | 'reserved' | 'unfavorable'
  note?: string
  recordedById?: Id                // mode atelier
}

// ── Le pouvoir ───────────────────────────────────────────────
interface Mandate extends Entity {
  title: string
  holderId: Id
  originDecisionId: Id             // le mandat EST né d'une décision — cliquable
  domain: { circleIds: Id[]; tags: string[] }
  startsAt: ISODate
  endsAt: ISODate                  // toujours borné
  electorCircleId: Id              // corpus d'élection = corpus de révocation
  nominationMethod: 'ratified-self' | 'election-no-candidate' | 'nuanced-vote' | 'consent' | 'draw' | 'rotation'
  reports: MandateReport[]
  status: 'proposed' | 'active' | 'expired' | 'revoked'
  // AUCUN agrégat/jauge : la fiche affiche des faits comptés (« Exercice du mandat »),
  // jamais synthétisés ; le flux public = « Feux de la rampe »
}
interface MandateReport { dueAt: ISODate; deliveredAt?: ISODate; content?: string }
interface MandateDraft {
  title: string
  domainCircleIds: Id[]
  domainTags: string[]
  durationDays: number
  reportEveryDays?: number
}

// ── Les textes ───────────────────────────────────────────────
interface TextDoc extends Entity {
  slug: string
  title: string
  role: 'pact' | 'reference'
  description: string              // Pacte seedé : « Notre contrat social — sacralisé, jamais immuable »
  provenance?: Provenance
}
interface Provenance {
  sources: { title: string; url: string; date?: string; version?: string }[]
  voteRecord?: {
    url: string; modeParams: string; period: string
    result: { for: number; against: number; invalid?: number; wotSize: number; thresholdRequired: number; status: string }
  }
  contributors?: string[]
  notes?: string                   // typos des textes votés : SIGNALÉES, jamais corrigées sans vote
}

type InertiaPreset = 'low' | 'standard' | 'high' | 'max'
// Câblage RÉEL : low={M:50,G:0.1} standard={M:50,G:0.2} high={M:60,G:0.4} max={M:66,G:0.6} (B=0.1, C=0)

interface Clause extends Entity {
  docId: Id
  section: string
  position: number
  code: string
  title: string
  inertia: InertiaPreset
  currentVersionId?: Id
  settingKey?: string              // clauses du Pacte : ex. 'triage.smallGroupMax'
}

interface ClauseVersion extends Entity {
  clauseId: Id
  decisionId: Id                   // TOUTE version naît d'une décision (y compris fondatrice seedée)
  versionLabel: string
  content: string
  settingValue?: Json
  status: 'current' | 'proposed' | 'superseded' | 'rejected'
  adoptedAt?: ISODate
}

// ── Modalités & vote ─────────────────────────────────────────
type VoteMethod = 'consent' | 'binary' | 'nuanced' | 'parametric' | 'election'
// UI : consentement · pour/contre (outil de dernier recours) · nuancé ·
//      « Réglage collectif » (au curseur) · élection

interface Protocol extends Entity {
  name: string
  method: VoteMethod
  description: string
  durationDays: number
  ballot: 'open' | 'secret'        // décidé par le Pacte — défaut 'open' (feux de la rampe).
                                   // v2 mono-poste : « secret d'affichage — démo » (décomptes agrégés
                                   // et commentaires anonymisés seuls) ; crypto réelle avec la synchro (v3)
  formula: FormulaParams
  modeParams: string               // DSL DÉRIVÉ affiché : 'D30M50B.1G.2S.1' — jamais édité à la main
  pactClauseId?: Id                // référencé par le Pacte ⇒ le modifier = amender le Pacte
}
interface FormulaParams {
  majorityPct: number              // M
  baseExponent: number             // B (défaut 0.1)
  gradientExponent: number         // G — l'inertie
  constantBase: number             // C (défaut 0)
  smithExponent?: number
  techcommExponent?: number
  nuancedMinParticipants?: number
  nuancedThresholdPct?: number
  parametricMinParticipants?: number   // quorum du réglage collectif — non atteint ⇒ rejected au geste
  electionMinParticipants?: number     // quorum de l'élection — non atteint ⇒ rejected
  tieBreak?: 'runoff' | 'draw'         // départage d'élection DÉCIDÉ D'AVANCE par le Pacte
                                       // (défaut 'runoff' : session de départage chaînée entre ex æquo ;
                                       // 'draw' : tirage au sort entre ex æquo — jamais choisi par le moteur)
}

interface VoteSession extends Entity {
  decisionId: Id
  protocolId: Id
  corpusPersonIds: Id[]            // SNAPSHOT à l'ouverture — UI : « liste arrêtée le … »
  corpusSize: number               // W — plus jamais 0 (lettre réservée à l'Atelier)
  opensAt: ISODate
  closesAt: ISODate
  status: 'open' | 'frozen' | 'closed'
  // 'frozen' : PARAMÉTRIQUE uniquement — à closesAt les votes sont figés, la médiane figée est affichée,
  // et la session ATTEND le geste humain du garant (relance au Fil s'il tarde).
  crystallizedById?: Id            // le geste « Cristalliser » : qui, quand — daté et signé
  crystallizedAt?: ISODate
  outcome?: 'adopted' | 'rejected' | 'tie'
  // 'tie' : élection à égalité — l'outil ne départage JAMAIS : session de départage chaînée
  // (restreinte aux ex æquo) ou tirage au sort SI le protocole l'a prévu (tieBreak).
  // Consent/nuancé/binaire : open→closed automatique à closesAt (le moteur constate un seuil
  // sur un contenu formulé par des humains). Paramétrique : open→frozen→closed (geste).
  // INVARIANT : tallies, histogrammes ET médianes TOUJOURS recalculés depuis Vote[] — jamais dénormalisés.
}

interface Vote extends Entity {
  sessionId: Id
  voterId: Id
  value?: 'for' | 'against' | 0 | 1 | 2 | 3 | 4 | 5
  // nuancé UI : 0 Pas du tout · 1 Plutôt non · 2 Réservé · 3 Plutôt oui · 4 Oui · 5 Tout à fait
  values?: number[]                // paramétrique — ordre de paramSpec.params HORS derived
  choicePersonId?: Id              // élection — désignation ; vote déposé sans choicePersonId = BLANC
                                   // (compte pour la participation, pas pour la désignation)
  // INVARIANT : EXACTEMENT UN de value / values / choicePersonId selon la méthode (validé par le store) —
  // sauf blanc d'élection (aucun des trois, méthode 'election' seulement)
  comment?: string                 // OBLIGATOIRE si négatif (against, 0, 1)
  supersedesVoteId?: Id            // re-vote audité — PRIVACY : la chaîne n'est JAMAIS affichée
                                   // publiquement sous le nom d'un votant ; visible du SEUL auteur ;
                                   // même en ballot 'open', seul le DERNIER vote actif apparaît sous un nom,
                                   // le décompte agrégé est recalculé (« pardonner les positions »)
  recordedById?: Id                // mode atelier
}

// ── Réglages résolus du Pacte (fonction pure — PAS une table settings) ──
interface CollectiveSettings {     // resolveSettings(pactClauses, versions)
  triage: {
    smallGroupMax: number          // défaut 5
    collectiveMin: number          // défaut 50
    consentMax: number             // défaut 7
    objectionWindowHours: number   // défaut 48
    adviceWindowHours: number      // défaut 72
    framingDays: number            // défaut 14 — « s'instruire et formuler des contre-propositions »
    concernEscalateRatio: number   // défaut 0.5 — atteint ⇒ traitement OBLIGATOIRE (garde state.ts)
    recurrenceThreshold: number    // défaut 3
    reviewDelayDays: number        // défaut 90
    requireEffects: 'none' | 'structural' | 'binding'
    // clé 'triage.requireEffects' — gouverne la garde matière des sessions COLLECTIVES :
    // 'binding' = binding ET structural (défaut association/cooperative/commune/free-currency/symmetric) ;
    // 'structural' = structural seul ; 'none' seedé dans blank et informal.
    // Sous garde, structural ⇒ ≥1 effet avec cible mesurable (target).
  }
  protocolByRange: {
    consent: Id                    // OBLIGATOIRE — invariant : tout collectif a un protocole Consentement
                                   // (tout gabarit le seede, l'import le valide)
    nuanced?: Id
    large?: Id                     // ex-inertialBinary : le nom ne présume plus la méthode.
                                   // Gabarits standard → nuancé ; free-currency → binaire inertiel
    parametric?: Id                // « Réglage collectif »
    election?: Id
    clauseByInertia?: Record<InertiaPreset, Id>   // l'héritage Ğ1 vit ici, intact
  }
  // REPLI SPÉCIFIÉ (resolveSettings) : toute clé optionnelle non résolue retombe sur consent ;
  // consent introuvable (bundle corrompu) ⇒ le triage route 'advice' avec bannière
  // « Aucun protocole — crée-le ou décide sur avis » — JAMAIS un crash.
}

// ── Moteurs purs (frontend/app/engine/ — implémentation UNIQUE, vitest) ──
// threshold.ts   : wotThreshold(W,T,{M,B,G,C}) — port exact de threshold.py (ceil) ;
//                  + smithThreshold, techcommThreshold, nuancedResult (histogramme 6 niveaux exposé) ;
//                  + electionResult(votes, corpus, formula) → { winnerId } | { outcome:'tie', exAequoIds }
//                    | { outcome:'rejected' } — pluralité simple des derniers votes actifs, blancs comptés
//                    en participation, quorum electionMinParticipants, JAMAIS de départage moteur.
//                  SPEC EXÉCUTABLE : cas Forgeron W=7224 T=120 M50 B.1 G.2 ⇒ 94.
// parametric.ts  : validateVote(spec, values) — bornes/step/clamp dynamique sum100, sanitize NaN/inf ;
//                  resolveDerived(spec, values) — sum100 linéaire (100 − Σ autres), étiquette « calculé » ;
//                  medianByElement(votes) — MÉDIANE BASSE par curseur (pair ⇒ élément central inférieur :
//                  chaque valeur médiane est une valeur effectivement votée ⇒ step respecté par
//                  construction — « une position que chacun aurait pu proposer », testé) ;
//                  crystallize(spec, votes) — médiane basse des parts votées PUIS resolveDerived ;
//                  derived hors bornes ⇒ clamp à la borne + renormalisation proportionnelle des parts
//                  non-derived (somme 100 restaurée) — spec testée ;
//                  computeMyImpact(spec, resources, values, myAttr, corpusAttrs) — 'linear-share' :
//                  UNIQUEMENT si constraint='sum100' && resources.amount && impactAttrKey ;
//                  montant(p) = amount × p/100 ; maPart(p) = montant(p) × myAttr / Σ corpusAttrs ;
//                  par curseur + total, aux trois positions (mienne/médiane/baseline) ;
//                  myAttr absent ou Σ=0 ⇒ null — JAMAIS un chiffre inventé ;
//                  detectBimodality(values[]) — plus grand écart entre valeurs triées vs dispersion,
//                  jamais bloquant. Dégénérescences : 0 vote ⇒ baseline, jamais d'écran vide.
// triage.ts      : triage(input, ctx, settings) → Verdict (règles ci-dessous)
// modeParams.ts  : parse/format du DSL
// settings.ts    : resolveSettings(clauses, versions) → CollectiveSettings (défauts + replis spécifiés)
// state.ts       : canTransition(decision, to, ctx: { concerns: Concern[], settings: CollectiveSettings,
//                    session?: VoteSession, children?: Decision[], assents?: Assent[],
//                    objections?: Objection[] }) — machine à états UNIQUE, gardes :
//                  windowSuspendedAt (frontière) ; affluence (ratio ⇒ élargir ou scopeKeptNote) ;
//                  matière (requireEffects) ; ressources (weight != light hors solo) ;
//                  Assent (fenêtres non-easy) ; dossier (framing→closed si children terminaux, geste) ;
//                  cristallisation (paramétrique : closed exige crystallizedById).
// impact.ts      : computeConcerned(scope, circles, mandates) → { personId, reason }[]
// app/lexicon.ts : source UNIQUE des libellés français + test vitest anti-lexique
//                  (déléguer, délégation, dette, retard, en souffrance, corpus, snapshot,
//                  CONTRE, verdict, triage, oligarchie, peuple adulte…).
//                  Règles de ton : (1) tutoyer la personne dans ses gestes ;
//                  (2) actes collectifs à l'infinitif (« Adopter notre Pacte ») ;
//                  (3) sujet des phrases générées = personnes/collectif, JAMAIS la formule.

interface TriageInput {
  title: string; tags: string[]
  scope: Decision['scope']
  reversibility: Reversibility
  weight: Weight
  urgent: boolean
  amendsClauseId?: Id
}
interface TriageContext {          // assemblé par le store, consommé pur — testable sans I/O
  myActiveMandates: Mandate[]
  otherActiveMandates: Mandate[]
  matchingClauses: Clause[]        // index Q0 « déjà décidé ? » — le MÊME index que Cmd+K
  similarRecentCount: number       // décisions adoptées partageant ≥2 tags sur 90 j
  similarRecordedCount: number     // consignations 'record' partageant ≥2 tags — maturation
  computedConcernedIds: Id[]
}
interface Verdict {
  route: DecisionRoute
  rule: TriageRule
  explanation: string              // UNE phrase française — affichée SEULE, sans code de règle
  protocolId?: Id
  windowHours?: number
  framingDays?: number
  reviewRequired: boolean
  engravingSuggested: boolean
  conservatoryChain?: boolean
  parametricHint?: boolean         // nombre/%/montant détecté ⇒ « C'est un réglage » mis en avant
  alternatives: { route: DecisionRoute; label: string; cost: string }[]
  // toujours présentes : « C'est un réglage — décidez au curseur » (parametric) ;
  // « Déjà tranché — je le consigne » (record) ; le binaire avec son coût (« deux camps, un perdant »)
  suggestion?: { kind: 'claim-mandate' | 'create-rule' | 'protocolize'; prefill: Json }
}

// ── Persistance locale & synchro future ──────────────────────
interface Bundle {                 // export/import ET seeds — le MÊME chemin de code
  schemaVersion: 2
  exportedAt: ISODate
  collective: Collective
  people: Person[]; circles: Circle[]
  decisions: Decision[]; concerns: Concern[]; objections: Objection[]; advices: Advice[]
  assents: Assent[]
  mandates: Mandate[]
  docs: TextDoc[]; clauses: Clause[]; versions: ClauseVersion[]
  protocols: Protocol[]; sessions: VoteSession[]; votes: Vote[]
}
// data/persistence.ts : IndexedDB (idb-keyval), clé `ld2:<collectiveId>`, débounce 500 ms.
// importBundle() valide protocolByRange.consent + pose Collective.lineage si bundle étranger.
// Prêt-synchro : UUIDv7, updatedAt (LWW), archivedAt, Vote/Advice/Objection/Assent append-only.
// Contrat des stores Pinia inchangé — l'adaptateur FastAPI futur ne change AUCUN écran.
// NOTES contrat : ballot 'secret' devra devenir cryptographique côté serveur (v3) ;
// la privacy des chaînes supersedesVoteId (visibles du seul auteur) est un invariant du contrat.
```

## Cycle de vie

ÉMERGENCE — la capture : une phrase dans « Qu'est-ce que tu décides ? » (Fil ou FAB). Crée Decision{status:'draft'}. Dix secondes visées, aucun autre champ obligatoire. Alternative permanente : « Déjà tranché — je le consigne » (route 'record') → adopted immédiat avec decidedHow, zéro fenêtre — l'observatoire en un geste, 2 gestes comme le solo.

TRIAGE — le chemin : triage() pur recalcule à chaque geste ; valider fait passer draft → l'état de départ de la route. Toujours expliqué (une phrase française, jamais de code), toujours contournable (alourdir libre 1 tap ; alléger = note obligatoire + fenêtre d'objection imposée — asymétrie journalisée). La ligne « Ce que ça engage » se DÉPLIE en suggestion si weight binding/structural hors solo mais ne bloque JAMAIS le tunnel ; « Aujourd'hui : … » suggéré sur advice/collective.

FORMULATION (route collective, poids binding/structural) : status 'framing' — « N jours pour s'instruire et formuler des contre-propositions » (framingDays, défaut 14). La matière (brief) s'édite ICI ; contre-propositions bornées (ClauseVersions ou variantes de body avec diff, contenu d'abord, auteur replié). Sur toute décision structural en framing : « Découper en micro-décisions » — chaque élément part en /decider?parent=&chain=element avec son propre triage/état/modalité ; les concernés pondèrent leurs enjeux (Concern.priority). GARDES D'OUVERTURE (framing→voting ou draft→voting, state.ts) : (a) matière — ≥1 effet recherché exigé si weight ∈ requireEffects du Pacte ('binding' couvre binding+structural ; 'none' dans blank/informal) ; structural sous garde ⇒ ≥1 effet mesurable (target) ; (b) ressources — resources.note exigé si weight != 'light' hors solo. Les deux gardes vivent à l'ouverture, jamais au tunnel.

DÉLIBÉRATION — selon la route :
- 'solo' selfOnly : draft → adopted immédiat (2 gestes). Trace facultative, visibility 'private' par défaut.
- 'record' : draft → adopted immédiat, decidedHow posé, trace pure.
- 'mandate' et solo-avec-tiers-dérogatoire : draft → objection (fenêtre 48 h, concernés notifiés au Fil, « Ça me va / J'objecte » — le « Ça me va » crée un Assent). À échéance sans objection ouverte : easy ⇒ adopted (silence vaut accord) ; costly/irreversible ⇒ adopted SEULEMENT si ≥1 Assent d'un concerné ≠ auteur, sinon la fenêtre se PROLONGE d'un cran (+objectionWindowHours, relance au Fil) — jamais d'adoption par pur silence hors easy. Objection 'content' maintenue → escalade framing/voting. Objection 'boundary' → windowSuspendedAt, compte à rebours SUSPENDU.
- 'advice' : draft → advice (72 h) ; avis déposés ; l'auteur décide → adopted (avis annexés). Objection formelle → escalade collective/consent.
- 'collective' : framing? → voting : VoteSession créée, liste arrêtée (union calculée + déclarés avant arrêt), W fixé. Modalités : consentement (zéro objection maintenue) ; nuancé (nuancedResult, histogramme visible en cours) ; binaire inertiel (votesFor ≥ wotThreshold) ; PARAMÉTRIQUE : curseurs nommés + statu quo + « calculé » + faisceau + médiane vivante + « Pour moi » + Explorer — values[] révisable jusqu'à closesAt (supersedesVoteId, historique visible du seul auteur) ; élection (désignation par avatar, blanc possible).
  CLÔTURE : consent/nuancé/binaire → automatique à closesAt (le moteur constate un seuil sur un contenu formulé par des humains). PARAMÉTRIQUE → à closesAt la session passe 'frozen' (votes figés, médiane figée affichée) puis le GARANT cristallise (« Cristalliser » → 'closed', crystallizedById/At, médiane basse = position adoptée ; quorum parametricMinParticipants non atteint ⇒ rejected constaté au même geste ; relance au Fil s'il tarde ; bimodalité ⇒ le geste affiche « Deux positions distinctes — cristalliser quand même / scinder / reformuler », jamais d'automatisme). ÉLECTION → automatique (pluralité, quorum electionMinParticipants) SAUF égalité : outcome 'tie', l'outil ne départage jamais — session de départage chaînée entre ex æquo, ou tirage au sort si le Pacte l'a prévu (tieBreak).

GARDE D'AFFLUENCE (transversale, state.ts) : si |déclarés| ≥ concernEscalateRatio × |calculés|, la fenêtre/session NE PEUT PAS se clore sans traitement — élargir d'un cran (1 tap, re-triage journalisé) OU scopeKeptNote motivée publique.

ADOPTION : decidedAt posé. Micro-célébration : tampon 井 « Décidé » + « C'est décidé — et révisable, comme tout ici ». amendsClauseId → ClauseVersion 'current' automatique. createsMandate → Mandate 'active'. structural/irreversible → review.dueAt + gravure proposée. sunsetAt optionnel. Mode secret : décomptes agrégés seuls (« secret d'affichage — démo »).

RÉALISATION / ÉVALUATION : stewardIds réalisent ; measurerIds constatent (Effect.measured). À review.dueAt : « L'ÉPREUVE DU RÉEL » — « Le réel a-t-il suivi ? », tableau recherché/cible/constaté AU-DESSUS des 3 boutons « Ça tient / À revoir / À révoquer » ; écart à baselineNote relu ; ressources réexaminées. 'confirmed' : review close (one-shot → 'closed') ; 'revise'/'revoke' : décision enfant chaînée sous protocole et inertie D'ORIGINE.

DOSSIER DÉCOUPÉ : quand tous les éléments sont en état terminal, carte au Fil du garant « Le dossier est complet — clore et publier la cartographie de clôture » ; le geste fait framing→closed du parent, la cartographie devient le PV du dossier. Jamais automatique.

RÉVISION / RÉVOCATION : toute décision adoptée porte « Remettre en question » — /decider pré-rempli, protocole et inertie d'origine affichés. Enfant 'revocation' adoptée → parent 'revoked' (« Révoquées — ce qu'on en a appris »).

URGENCE : « C'est urgent » — si non irréversible, voie la plus légère MAINTENANT (badge « Décidé en urgence — le collectif ratifie », fenêtre ×2) + ratification chaînée automatique. Irréversible ⇒ refusé : « Irréversible : l'urgence ne peut pas contourner le collectif ».

TRANSMISSION : 'transmit' → 'transmitted' (terminal pour l'auteur) ; le mandataire reprend ou l'auteur conteste le mandat.

MACHINE À ÉTATS UNIQUE (engine/state.ts) : draft→{advice,objection,framing,voting,adopted,transmitted} ; advice→{adopted,voting} ; objection→{adopted,framing,voting} ; framing→{voting,closed} (closed : dossier complet, geste) ; voting→{adopted,rejected} ; adopted→{revoked,closed} ; + gardes windowSuspendedAt, affluence, matière, ressources, Assent, cristallisation. Toute mutation passe par canTransition(decision, to, ctx).

## Algorithme de triage

MOTEUR : engine/triage.ts — fonction PURE triage(input, ctx, settings) → Verdict. Tous les seuils viennent des clauses du Pacte (resolveSettings, replis spécifiés → consent) — le triage est gouverné par ceux qu'il gouverne. « Triage » et « verdict » ne sortent JAMAIS du code : l'UI dit « Le chemin ». La carte affiche l'explanation SEULE (code R* au dépliable « pourquoi ? », au PV et au journal de dérogation). Sujet grammatical : toujours le collectif ou la légitimité, jamais la formule.

ENTRÉES EXPLICITES (3 chips max) : 1. Concernés → scope (pré-calculé tags×domains, corrigeable, plancher non restrictible). 2. Réversible ? → easy/costly/irreversible. 3. Ça engage quoi ? → light/binding/structural — si binding/structural hors solo, la ligne « Ce que ça engage » (resources.note + montant/unité) se DÉPLIE en suggestion, jamais bloquante (l'exigence vit à la garde d'ouverture de state.ts). ENTRÉES INFÉRÉES : mandats couvrants, clauses qui matchent (index Q0 = Cmd+K), récurrence, consignations similaires, « C'est urgent », clause visée, nombre/%/montant dans la phrase ⇒ parametricHint.

RÈGLES, DANS L'ORDRE — première qui matche (libellés français FINAUX) :
- R-U URGENCE (overlay) : urgent && !irreversible ⇒ voie la plus légère MAINTENANT (badge « Décidé en urgence — le collectif ratifie », fenêtre ×2) + ratification chaînée auto. urgent && irreversible ⇒ REFUS : « Irréversible : l'urgence ne peut pas contourner le collectif. »
- R0a DÉJÀ DÉCIDÉ : clause en vigueur couvre ⇒ « C'est déjà décidé (E9, Acte Forgeron) — agis. » avec lien, OU « Conteste la règle » ⇒ R3 avec l'inertie de la clause.
- R0b MON MANDAT : ⇒ 'mandate' : « Ton mandat Modération couvre — décide, c'est tracé. » Trace obligatoire aux feux de la rampe + fenêtre d'objection 48 h.
- R0c MANDAT D'UN AUTRE : ⇒ 'transmit' : « Le mandat de Cgeek couvre — transmets-lui. » Alternative : « Conteste ce mandat ».
- R2 MOI SEUL : selfOnly ⇒ 'solo' : « Personne d'autre n'est concerné — décide. » Zéro fenêtre ; si irréversible, revoyure pré-cochée retirable (sur soi l'outil ne s'interpose pas).
- R3 CLAUSE VISÉE : amendsClauseId ⇒ 'collective', protocole = clauseByInertia[clause.inertia] (repli → consent si absent) : « Tu proposes une version de E9 — son inertie s'applique : vote de ceux qu'elle gouverne. » L'héritage Ğ1 (binaire inertiel des clauses) survit ICI, intact.
- R4 PETIT CERCLE RÉVERSIBLE : easy && |concernés| ≤ smallGroupMax(5) ⇒ 'advice' : « Réversible et 3 personnes concernées — demande leur avis puis décide. » 72 h ; objection formelle ⇒ escalade R5/consent.
- R5 COLLECTIF (défaut de l'escalade) : sinon ⇒ 'collective', modalité par taille : ≤ consentMax(7) → consentement (« Vous êtes 6 — un tour d'accord suffit : sans objection, c'est adopté. ») ; ≤ collectiveMin(50) → nuancé (« Vous êtes 23 — vote nuancé : chacun se prononce en nuances, pas en camps. ») ; > collectiveMin → protocole protocolByRange.large résolu par le Pacte (repli → consent) — gabarits standard : « Vous êtes plus de 50 — vote nuancé, la modalité que votre Pacte a choisie. » ; free-currency/Ğ1 : binaire inertiel hérité (« … la formule d'inertie que votre communauté a adoptée »). structural ⇒ + framing + matière selon requireEffects + revoyure + gravure suggérée + inertie haute ; irreversible ⇒ revoyure obligatoire. Si parametricHint : la bascule « régler plutôt que trancher » proposée sous le chemin.
- R6 RÉCURRENCE & MATURATION (overlay, jamais bloquant) : similarRecentCount ≥ 3 ⇒ « 3e fois — réclame un mandat » (createsMandate pré-rempli) ou « crée une règle » ; similarRecordedCount ≥ 3 (≥2 tags partagés) ⇒ « Cette pratique est mûre — protocolise-la en quelques phrases » (pré-remplit une clause de Pacte depuis les traces réelles).

ALTERNATIVES PERMANENTES (« Je choisis autrement ») : « C'est un réglage (montant, taux, répartition) — décidez au curseur » (parametric, mise en avant si parametricHint) ; « Déjà tranché — je le consigne » (record) ; le binaire pour/contre avec son coût affiché (« deux camps, un perdant ») ; chaque alternative avec son coût.

DÉROGATION ASYMÉTRIQUE : ALOURDIR libre — 1 tap journalisé. ALLÉGER = overrideNote obligatoire + fenêtre d'objection 48 h imposée, affiché en évidence. MÊME ASYMÉTRIE AU PÉRIMÈTRE : affluence atteinte ⇒ élargir (1 tap) ou scopeKeptNote motivée publique — jamais d'évaporation silencieuse. Le chemin n'est jamais un juge : il recommande, explique, et laisse faire.

MÉTRIQUE PRODUIT : chemin nominal solo = phrase + Entrée + bouton = 2 gestes, < 10 secondes ; le tunnel n'affiche JAMAIS un champ obligatoire qui ne change pas le chemin (les gardes de matière et de ressources vivent à l'ouverture de fenêtre/session, pas au tunnel) ; brief et curseurs vivent APRÈS le tunnel.

## Modalités

CINQ MODALITÉS, UNE BOÎTE À OUTILS HIÉRARCHISÉE PAR LA DOCTRINE (chaque protocole est un Protocol nommé, référencé par une clause du Pacte — le modifier = amender le Pacte ; Protocol.ballot open/secret décidé par le Pacte, défaut open « feux de la rampe », secret v2 = « secret d'affichage — démo » ; dans TOUTES les modalités, l'historique de re-vote supersedesVoteId n'est visible que de son auteur — seul le dernier vote actif apparaît sous un nom) :

1. CONSENTEMENT (« Ça me va / J'objecte ») — proposé par R5 pour ≤ consentMax(7), par les escalades d'objection, et REPLI universel de protocolByRange (tout collectif en a un — invariant). Zéro vote : le « Ça me va » est stocké (Assent), tour d'objections argumentées, zéro objection maintenue = adopté. Hors du réversible, ≥1 Assent est requis à l'échéance (sinon prolongation) — l'accord est un geste, pas une absence.

2. NUANCÉ 6 NIVEAUX — proposé par R5 pour 8..collectiveMin(50) ET défaut des grands corpus (protocolByRange.large des gabarits standard). Libellés : 0 « Pas du tout » · 1 « Plutôt non » · 2 « Réservé » · 3 « Plutôt oui » · 4 « Oui » · 5 « Tout à fait » — jamais « CONTRE ». Commentaire OBLIGATOIRE sur 0-1. Histogramme des 6 niveaux visible en cours et à la clôture + commentaires en liste — jamais un simple adopté/rejeté. Valeurs 0-5 et résultats Ğ1 intacts. Clôture automatique à closesAt.

3. PARAMÉTRIQUE — « Réglage collectif » / décider au curseur. JAMAIS routé d'office : proposé dans « Je choisis autrement », mis en avant si parametricHint. paramSpec : ≤7 curseurs à libellés métier ('slider' borné ou 'share' répartition), contrainte 'sum100' avec EXACTEMENT une part 'derived' (variable d'absorption, résolue 100 − Σ autres) — étiquettes « contrainte / calculé ». Salle : curseurs + statu quo + bloc calculé non éditable + faisceau translucide + médiane trait épais + carte « Pour moi » (linear-share spécifié : quote-parts de resources.amount au prorata de l'attribut déclaré — sans attribut, jamais de chiffre inventé) + « Explorer » (sandbox « exploration — ne compte pas »). Vote values[] révisable jusqu'à la clôture. Agrégation NOMMÉE : « Le collectif retient la médiane de chaque curseur — robuste aux extrêmes, et c'est une position que chacun aurait pu proposer. » (médiane BASSE : l'invariant est vrai par construction, step respecté). À closesAt : session 'frozen' — puis CRISTALLISATION = GESTE HUMAIN DATÉ du garant (« Cristalliser », crystallizedById/At ; quorum parametricMinParticipants sinon rejected ; relance au Fil ; bimodalité affichée en évidence au moment du geste : « cristalliser quand même / scinder / reformuler » — jamais d'automatisme). Pour les DOSSIERS complexes : le découpage en micro-décisions (chainKind 'element', « décision incrémentielle ») combine plusieurs modalités élément par élément, avec pondération (Concern.priority) et cartographie de clôture publiée par le geste de clôture du garant.

4. ÉLECTION — reléguée aux seules problématiques d'échelle et de mandats : la nomination EST une décision createsMandate (6 méthodes : auto-désignation ratifiée, élection sans candidat, nuancé, consentement, tirage au sort, rotation). Jamais proposée pour une question de fond. CODÉE : Vote.choicePersonId désigne une personne du corpus ; vote déposé sans désignation = BLANC (participation, pas désignation) ; clôture automatique à closesAt : quorum electionMinParticipants sinon rejected, désignation à la PLURALITÉ simple des derniers votes actifs ; ÉGALITÉ ⇒ outcome 'tie' — l'outil ne départage JAMAIS : session de départage chaînée entre ex æquo (pré-remplie), ou tirage au sort entre ex æquo UNIQUEMENT si le protocole du Pacte l'a prévu d'avance (tieBreak:'draw'). Règle affichée en une phrase dans la salle.

5. BINAIRE INERTIEL (pour/contre + Seuil = C + B^W + (M + (1−M)(1−(T/W)^G))·max(0,T−C)) — HÉRITAGE REMIS À SA PLACE : proposé par R3 pour l'amendement des clauses Ğ1 (clauseByInertia — les 59 Forgeron votées ainsi, D30M50B.1G.2 intact) et par R5 >50 UNIQUEMENT dans free-currency. Partout ailleurs : disponible dans « Je choisis autrement » avec son coût (« deux camps, un perdant ») ; description seedée : « l'outil de dernier recours des très grands corps — remis à sa place dans la boîte à outils ». L'Atelier des formules reste son école (seul lieu des lettres W/T/M/B/G/C et de KaTeX).

RÉSOLUTION : protocolByRange { consent (obligatoire), nuanced?, large?, parametric?, election?, clauseByInertia? } — Ids résolus par le Pacte, repli spécifié → consent, ajustables en alourdissant. L'adoption applique automatiquement (clause current, mandat créé) — zéro bouton d'acceptation manuel ; tallies/médianes toujours recalculés depuis Vote[].

## Périmètres et inclusion

LE MOT INSTALLÉ : « Périmètre » = qui est concerné par CETTE décision (panneau de la fiche, frise du chemin) ; « Cercle » = groupe durable de personnes (entité Circle, listes nominatives auditables). Les 3 vecteurs doctrinaux : ÉCHELLE D'ENJEU portée par weight/urgent/reversibility ; THÉMATIQUE par tags/domains (suggestion, jamais un droit) ; TOPOLOGIQUE déclaratif via Circle.kind 'place'|'theme'|'team' (iconographie, groupement, ordre des suggestions — jamais un droit ; hint non bloquant à 12 membres pour les 'team'). Le renommage réserve la place des vecteurs calculés (géo/WoT, v3) sans casser un libellé.

DEUX PORTES, UNE LISTE, CHAQUE ENTRÉE SOURCÉE (Concern horodaté) :
- CONCERNÉS EN PREMIER LIEU (origin 'computed', pastille pleine) : à la validation du chemin, engine/impact.ts calcule l'union CALCULABLE (membres des cercles du scope ∪ personnes nommées ∪ titulaires des mandats intersectants). Chaque inclusion porte sa raison en clair — auditable en un tap, jamais « la machine t'a désigné ». PLANCHER ANTI-EXCLUSION : élargir libre, JAMAIS restreindre sous le calcul.
- CONCERNÉS EN SECOND LIEU (origin 'declared', main levée) : bouton « Ça me concerne » sur toute décision visible, SANS justification — un droit, pas une demande ; motif optionnel public. Déclaré AVANT la liste arrêtée ⇒ vote ; APRÈS ⇒ voix consultative — règle affichée en compte à rebours.

OBJECTION DE FRONTIÈRE (suspensive — la contestation des frontières prime sur le fond) : « J'ai été oublié / Untel manque » ⇒ windowSuspendedAt, compte à rebours SUSPENDU (pastille pause, « compte à rebours suspendu — frontière contestée »), priorité maximale au Fil de l'auteur, jusqu'à intégration ou réponse motivée.

MONTÉE D'ÉCHELLE — DÉCIDÉE, JAMAIS ÉVAPORÉE : si |déclarés| ≥ concernEscalateRatio(0.5) × |calculés|, bannière « Le périmètre déborde — élargis d'un cran » ET garde dans state.ts : la fenêtre/session ne peut pas se clore sans traitement — élargissement au cercle parent (couches d'oignon, 1 tap, re-triage journalisé — chemin nominal), ou maintien avec scopeKeptNote motivée PUBLIQUE, affichée en évidence comme un allègement. L'asymétrie de dérogation appliquée au lieu doctrinal de la subsidiarité.

GRADIENT D'ENGAGEMENT (affiché sur la fiche) : observateur (lecture) < concerné (avis, objection, « Ça me va ») < lié (vote — liste arrêtée) < garant/mesureur (stewardIds réalisent et cristallisent, measurerIds constatent).

PONDÉRATION PERSONNELLE DES ENJEUX : sur un dossier découpé, chaque concerné pose Concern.priority 0-3 (« pondère tes enjeux ») depuis le Fil ou la fiche — nourrit la hiérarchie des enjeux de la cartographie de clôture, jamais le droit de vote.

ANTI-BRIGADING PAR CONSTRUCTION : déclarations publiques + liste arrêtée + entrée tardive consultative + escalade non-ignorable — et la formule inertielle absorbe l'afflux (gonfler W sans que T suive fait MONTER le seuil). ANTI-FLICAGE (ligne rouge) : hors mandat, trace facultative et visibility choisie ('private' par défaut en solo) ; sous mandat, trace obligatoire visible du cercle électeur — la redevabilité s'applique au pouvoir, jamais aux personnes ; Person.attributes jamais affichés à autrui ni agrégés ; l'historique des re-votes n'est jamais opposable à une personne (visible du seul auteur).

FRACTALITÉ : Collective.lineage (« essaimé de … ») mémorise l'essaimage par bundle ; AUCUNE structure fédérale dans le modèle (choix doctrinal documenté) — pattern v3 : une grappe = un collectif ordinaire dont les membres sont des mandataires d'autres collectifs, la fédération = échange de bundles/PV, jamais une table.

## Instruction et évaluation

LE PRINCIPE : « Il n'est pas consulté, il est instruit » — pas de vote collectif engageant sans matière, et la même matière sert l'évaluation : la boucle d'« acquisition collective des expériences » est fermée par construction, sans jamais devenir plus lourde que l'enjeu.

LA MATIÈRE (Decision.brief) : contexte, sources {label,url}, symptômes vs causes (la qualification doctrinale du sujet), et effects — les EFFETS RECHERCHÉS { label une ligne, target cible mesurable optionnelle, measured constaté }. GARDE DÉFINITIVE (state.ts, ouverture de session collective UNIQUEMENT — framing→voting / draft→voting) : ≥1 effet recherché exigé si weight ∈ clause de Pacte 'triage.requireEffects' ('binding' = binding et structural, défaut des gabarits structurés ; 'structural' ; 'none' seedé dans blank ET informal — le collectif règle sa propre exigence) ; sous garde, structural ⇒ ≥1 effet avec cible mesurable. Le weight 'light' (le trio, « on part le 12 ») n'y est JAMAIS soumis ; le chemin solo/record ne voit JAMAIS ces champs — 2 gestes / 10 s préservés ; la matière s'écrit en framing, pas dans le tunnel.

LE BLOC « S'INSTRUIRE » (condensé — réparation intégrée) : en tête de la salle de vote et de la fiche (framing/voting) — un RÉSUMÉ COMPACT toujours visible avant les boutons : titre, effets recherchés, « Ce que ça engage » ; en DÉPLIABLES internes : body, versions + diff (contenu d'abord, auteur replié), avis et objections déposés, provenance (cas Ğ1 : voteRecord). Le bloc entier se replie automatiquement aux visites suivantes de la même personne (préférence locale par décision) — voter sur un titre nu reste structurellement impossible en collectif, re-voter reste léger (« agir d'abord, comprendre ensuite »). La phrase de fenêtre : « 14 jours pour s'instruire et formuler des contre-propositions ».

LE STATU QUO (double ancre SejeteralO) : Decision.baselineNote (« Aujourd'hui : … ») suggéré routes advice/collective — en-tête de fiche « Aujourd'hui → Proposé » ; ParamDef.baseline = marqueur « actuel » sur chaque curseur + colonne de la carte « Pour moi » ; les clauses gardent leur diff (qui EST leur baseline). Une décision se lit comme un écart au statu quo, mesurable.

LA DÉCISION CONNEXE (Decision.resources) : « Ce que ça engage » — note + montant/unité ('heures'|'€'|'DU'|libre). SUGGÉRÉE-dépliée au tunnel (jamais bloquante), EXIGÉE à la garde d'ouverture de fenêtre/session si weight != 'light' hors solo (même mécanique que la matière). Affichée fiche + résumé S'instruire + PV, réexaminée à l'épreuve du réel. Huile/eau : DU et € jamais convertis ni additionnés — agrégats de l'Observatoire par unité, lignes séparées. La paire doctrinale : stewardIds RÉALISENT (et cristallisent), measurerIds MESURENT. resources.amount est aussi le multiplicande de la carte « Pour moi » (linear-share).

L'ÉPREUVE DU RÉEL (Review) : à review.dueAt — « Le réel a-t-il suivi ? », tableau effet recherché / cible / constaté (Effect.measured saisi par les measurerIds) AU-DESSUS des 3 boutons « Ça tient / À revoir / À révoquer » ; écart à la baseline relu ; ressources confrontées. Jamais un jugement dans le vide : « évaluer les expériences avec la curiosité du chercheur », le droit de se tromper est une donnée.

L'OBSERVATOIRE (/decisions/observatoire) : comment nous décidons, DANS LES FAITS. Stats d'autonomie (ratio seul/mandat/avis/collectif/consigné, temps médians, participation, dérogations alourdies/allégées/périmètres maintenus, ressources engagées par unité) ; épreuves du réel rendues et dues ; « Révoquées — ce qu'on en a appris » ; élagage suggéré (protocole jamais invoqué, mandat sans trace). LA CONSIGNATION (route 'record') : « Déjà tranché — je le consigne » + decidedHow — consigner AVANT de protocoliser ; ratio consigné/outillé = maturité ; MATURATION (famille R6, jamais bloquante) : ≥3 consignations partageant ≥2 tags ⇒ « Cette pratique est mûre — protocolise-la en quelques phrases » pré-remplissant une clause de Pacte depuis les traces réelles. Le flux consigner→observer→protocoliser est le chemin ordinaire du produit, symétrique de R6 : le contrat social s'écrit au fil de l'eau, depuis la pratique.

## Navigation

SHELL : layouts/default.vue. Header : logo-stamp (icône puits, rotation -10°) + « libreDecision » ; WorkspaceSelector (collectifs locaux + finalité A1 en une ligne + « Créer un collectif ») ; recherche globale Cmd+K (index unique clauses/décisions/mandats = le MÊME index que Q0) ; MoodSwitcher ; bouton « Décider » (desktop). FAB « Décider » 56 px mobile, au-dessus du sceau. Sceau hexagramme #48 Tsing 井 fixe bas-droite (géométrie SVG canonique). layouts/bare.vue pour /creer.

SIDEBAR — 4 ENTRÉES, pas une de plus :
1. Aujourd'hui (/) — le Fil + la capture. À 3 personnes, c'est tout l'outil.
2. Décisions (/decisions) — le registre + l'Observatoire.
3. Textes (/textes) — les documents sous vote permanent, Pacte épinglé.
4. Mandats (/mandats) — les pouvoirs confiés.

DISSOLUTIONS CONSERVÉES : protocoles dans le Pacte + étape modalité du chemin ; Atelier des formules = /textes/formules (lié depuis CHAQUE seuil, seule maison des lettres W/T/M/B/G/C) ; Sanctuaire = badge 井 + fiche de preuve + filtre ; pas de page réglages (les réglages SONT le Pacte) ; préférences locales = /donnees (mood, export/import, identité, attributs personnels, mode atelier).

ROUTES : / — Aujourd'hui · /decider — Le chemin (pré-remplissable ?clause= ?parent=&chain= ?mandat=) · /decisions — registre · /decisions/observatoire — L'Observatoire · /decisions/[id] — fiche décision (URL permanente) · /decisions/[id]/vote — salle de vote · /textes — bibliothèque · /textes/formules — Atelier des formules · /textes/[slug] — document vivant · /mandats — liste · /mandats/nouveau — wizard · /mandats/[id] — fiche mandat · /creer — onboarding (bare) · /donnees — données locales.

COUCHES CODE : app/types/domain.ts (noyau) ; app/lexicon.ts (source unique des libellés FR + test anti-lexique) ; app/engine/ (threshold avec electionResult, parametric avec crystallize, triage, modeParams, settings, state avec ctx, impact — purs, vitest) ; app/data/ (persistence.ts IndexedDB + seeds/*.bundle.json + templates.ts 7 gabarits) ; app/stores/ (contrat commun prêt adaptateur FastAPI — invariants privacy inclus) ; app/composables/ (useMood, useSettings, useFeed, useSearch) ; app/components/ par domaine (chemin/, decisions/, votes/ dont Faisceau et Curseurs, texts/, mandates/, circles/, common/). Le Fil est un SÉLECTEUR PUR — zéro table notification (il dérive : fenêtres, votes, épreuves dues, rapports dus, frontières, affluences, cristallisations en attente, dossiers complets, suggestions R6/maturation). Budget 400 lignes max par page. CSS print A4 (PV + affiche de session).

## Écrans

### `/creer` — Créer un collectif

_Onboarding en 3 écrans — le premier usage EST le tutoriel ; s'ouvre sur le rêve_

Ouverture : « Une démocratie qui n'a jamais existé — à votre échelle. » Écran 1 : nom, couleur, mood. Écran 2 : gabarits en 7 cartes qui TRADUISENT — « Page blanche — observatoire d'abord » EN TÊTE (Pacte : clause « nous consignons nos décisions ; le reste s'écrira au fil de l'eau » + A1 finalité, protocole Consentement SEUL installé, routes solo/consignation/avis/consentement) ; puis Informel / Association / Coopérative / Commune / Communauté monnaie libre réétiquetés « point de départ — tout est amendable par décision » ; et « Institution symétrique » (Déclaration de symétrie, mandats Ambassadeur/Négociateur suggérés, première décision « Déclarer notre symétrie »). Chaque gabarit installe Pacte pré-rempli (préambule Autonomie–Équilibre–Liaison proposé et modifiable, clause A1, clauses settingKey dont requireEffects) + protocoles (au minimum Consentement — invariant) + cercle racine typé. Langage concret (« qui doit être d'accord ? »). Écran 3 : moi + membres initiaux. Imports « Explorer Duniter Ğ1 » / « Explorer l'Atelier du Canal » par le MÊME chemin qu'un import utilisateur. Atterrissage sur / avec la première décision proposée : « Adopter notre Pacte » (collective, consentement). Signature finale « Osons, nous verrons ». Réussite < 10 minutes, testée avec des non-théoriciens.

### `/` — Aujourd'hui

_L'écran quotidien unique : capture + Fil personnel — la boîte de réception de la démocratie_

Barre de capture sticky « Qu'est-ce que tu décides ? » (Entrée → /decider). Header « À toi de décider » avec compteur nu (jamais de rouge — interdits : dette, retard, en souffrance). Fil trié par échéance, sections vides masquées : fenêtres d'objection (« Ça me va / J'objecte » inline — le Ça me va crée un Assent ; compte à rebours ; pastille pause si frontière contestée ; mention « il manque un accord explicite » si fenêtre prolongée) ; avis demandés (3 boutons inline) ; votes ouverts (jauge miniature, lien salle) ; À CRISTALLISER (garant d'un réglage collectif figé : « Les votes sont figés — cristallise la médiane », relance persistante) ; DOSSIER COMPLET (garant : « Le dossier est complet — clore et publier la cartographie de clôture ») ; égalité d'élection à départager (session chaînée proposée) ; épreuves du réel dues (« Ça tient / À revoir / À révoquer » inline) ; rapports de mandat dus ; objections de frontière à traiter (priorité haute) ; périmètre débordant (élargir / maintenir motivé) ; pondération d'enjeux demandée ; suggestions de la boucle apprenante (« 3e fois — réclame ce mandat », « Cette pratique est mûre — protocolise-la », « ce protocole n'a jamais servi — élague »). Ensuite l'activité du collectif (selon visibility). Tout est sélecteur Pinia pur. Mobile-first absolu.

### `/decider` — Le chemin (tunnel)

_Une phrase → un chemin exécutable en un tap — 10 s pour le routinier, 3 min pour le stratégique_

Phrase en haut (pré-remplie par capture ou query clause/parent/chain/mandat). Résultats Q0 « déjà décidé ? » inline dès la frappe. 3 chips : Concernés (avatars pré-calculés, plancher non restrictible), Réversible ?, Ça engage quoi ? — si binding/structural hors solo, la ligne « Ce que ça engage » se DÉPLIE en suggestion (note + montant/unité, jamais € et DU additionnés) mais ne bloque JAMAIS la validation (l'exigence vit à l'ouverture de fenêtre/session) ; « Aujourd'hui : … » suggéré sur avis/collectif. Toggle « C'est urgent » discret. Carte LE CHEMIN live : grande icône de route, l'explication en une phrase SEULE (aucun code R*), frise le périmètre / combien de temps / quel seuil, revoyure et gravure pré-cochées si exigées, UN bouton principal. « Je choisis autrement » toujours visible : alternatives avec coût, dont « C'est un réglage — décidez au curseur » (mise en avant si nombre/%/montant détecté), « Déjà tranché — je le consigne » (→ adopted immédiat + « comment ça s'est décidé »), binaire avec « deux camps, un perdant ». Alourdir 1 tap journalisé ; alléger = note + fenêtre imposée. « Pourquoi ? » dépliable (le code de règle y vit). Suggestions R6/maturation sous le chemin. Valider crée la Decision structurée dans le bon état.

### `/decisions` — Registre des décisions

_Toutes les décisions par état — SectionLayout conservé_

Pills d'état en français courant (préparation / avis / objection / formulation / vote / figé / en vigueur / clos / rejeté / révoqué) aux couleurs du cycle. Filtres : cercle (groupés par kind lieu/thème/équipe), tag, « me concerne », poids, gravées 井, consignées. Formes courtes des routes en pills (seul / sous mandat / transmis / sur avis / ensemble / consigné). Cartes : titre, icône de route, poids, échéance, jauge miniature, badge 井, badge « Décidé en urgence — le collectif ratifie ». Lien vers l'Observatoire. Zéro création ici — on crée par la capture.

### `/decisions/[id]` — Fiche décision

_URL permanente de l'objet pivot — progressive disclosure par état_

En-tête : titre, « Aujourd'hui → Proposé » si baselineNote, route en français, poids, badges (urgence, allègement avec note, périmètre maintenu avec scopeKeptNote en évidence, 井). Timeline verticale du cycle. Panneau PÉRIMÈTRE : concernés en premier lieu (pastille pleine, tap → raison) et en second lieu (main levée, note publique), « Ça me concerne », compte à rebours de la liste arrêtée, bannière d'affluence NON-IGNORABLE (élargir 1 tap / maintenir motivé), objection de frontière (pastille pause). Bloc S'INSTRUIRE (framing/voting) : résumé compact toujours visible (titre, effets, ce que ça engage) + dépliables (body, versions/diff auteur replié, avis/objections, provenance) — auto-replié aux visites suivantes. Selon l'état : bloc fenêtre (boutons inline, silence-vaut-accord affiché seulement si réversible, sinon « un accord explicite est requis » avec compteur d'Assents) ; bloc formulation (« N jours pour s'instruire… », « Découper en micro-décisions » sur structural) ; onglet ÉLÉMENTS si dossier (filtre « me concernent », états, échéances, pondération 0-3) + CARTOGRAPHIE DE CLÔTURE auto-générée + carte « Le dossier est complet — clore » quand tous terminaux (geste du garant) ; bloc session (lien salle, résultat, « Qui vote : N personnes — liste arrêtée le … », bandeau « Votes figés — en attente de cristallisation » si frozen) ; bloc vigueur (garants, mesureurs, ressources, sunset, épreuve du réel due : tableau recherché/constaté au-dessus des 3 boutons) ; chaînage cliquable. Poignée « Remettre en question » sur tout adopté (protocole et inertie D'ORIGINE affichés). Actions : Imprimer (PV A4), graver (« empreinte locale — démo »), micro-copy « à trancher en présence ? » sur structural. Budget 400 lignes.

### `/decisions/[id]/vote` — Salle de vote

_Le geste démocratique central — instruit, puis prononcé_

Bloc S'INSTRUIRE condensé en tête : résumé compact toujours visible avant les boutons (titre, effets recherchés, ce que ça engage) + dépliables (texte/diff, avis et objections, provenance) — replié automatiquement aux visites suivantes. « Qui vote : 11 personnes — liste arrêtée le 12/08 » (avatars). Selon la méthode : CONSENTEMENT (« Ça me va » stocké / « J'objecte » argumentée) ; NUANCÉ 6 niveaux (« Pas du tout » → « Tout à fait », commentaire obligatoire inline sur 0-1, HISTOGRAMME sous la jauge) ; BINAIRE hérité (jauge inertielle : arc animé, seuil qui descend quand la participation monte, phrase sujet-collectif : « 120 votants sur 7 224 : à si faible participation, il faut presque l'unanimité — 94 pour. Plus de monde vote, plus le seuil descend. », lien « comprendre ce seuil » → Atelier pré-réglé) ; PARAMÉTRIQUE « Réglage collectif » : un curseur par paramètre (libellé métier, marqueur statu quo creux, alternative clavier + champ numérique), bloc « calculé » non éditable (étiquettes contrainte/calculé), FAISCEAU strip-plot (points translucides anonymes, médiane basse trait épais), carte « POUR MOI » (ma position / médiane / statu quo — quote-parts de « ce que ça engage » au prorata de mon attribut déclaré, « estimation sur données déclarées », jamais de chiffre inventé), « EXPLORER » (sandbox « exploration — ne compte pas », partir de la médiane/du statu quo/de ma position, « En faire ma position »), phrase d'agrégation « Le collectif retient la médiane de chaque curseur — robuste aux extrêmes, et c'est une position que chacun aurait pu proposer », bannière bimodalité non bloquante ; à closesAt bandeau « Votes figés — le garant cristallise » puis bouton « CRISTALLISER » (garant seul, quorum vérifié, bimodalité rappelée : « cristalliser quand même / scinder / reformuler ») ; ÉLECTION (désignation par avatar, bouton « Vote blanc », règle en une phrase : « La personne la plus désignée est nommée ; en cas d'égalité, vous départagez — jamais l'outil » ; égalité ⇒ session de départage chaînée ou tirage au sort si le Pacte l'a prévu). Mode secret : décomptes agrégés et commentaires anonymisés seuls, « secret d'affichage — démo ». Mon vote remplaçable — « ton historique n'est visible que de toi » (chaîne supersedes jamais publique, dernier vote seul affiché sous un nom en ballot open). Bouton « Imprimer » : affiche de session A4. Clôture : distribution complète + arguments — jamais un simple adopté/rejeté ; adoption ⇒ application automatique + tampon 井 « C'est décidé — et révisable, comme tout ici ».

### `/decisions/observatoire` — L'Observatoire

_Comment nous décidons, dans les faits — le miroir et l'apprentissage_

Titre « L'Observatoire », sous-titre « comment nous décidons, dans les faits ». Stats d'autonomie : ratio seul/mandat/avis/collectif/CONSIGNÉ (consigné/outillé = maturité), temps médians par route, taux d'objection, participation, dérogations (alourdies/allégées/périmètres maintenus), ressources engagées PAR UNITÉ (heures / € / DU — lignes séparées, jamais additionnées). Épreuves du réel : dues, rendues (tenues/à revoir/révoquées). « Révoquées — ce qu'on en a appris » : chaque révoquée avec sa chaîne — curiosité du chercheur, pas de lugubre. Consignations : liste des « déjà tranché » avec decidedHow ; maturation : ≥3 consignations partageant ≥2 tags ⇒ carte « Cette pratique est mûre — protocolise-la en quelques phrases » (pré-remplit une clause de Pacte). Élagage : protocole jamais invoqué, mandat sans trace 6 mois → revue suggérée. Décisions jamais revues : relance puis archivage proposé. Filtres personne/cercle/tag/période. Graphiques sobres (skill dataviz).

### `/textes` — Textes

_La bibliothèque des documents sous vote permanent_

Pacte épinglé en tête : badge « les règles du jeu » + sous-titre « Notre contrat social — sacralisé, jamais immuable ». Cartes documents : titre, rôle, clauses, sessions en cours (mini-jauges), provenance Ğ1 (Certification 33 clauses, Forgeron 59 v2.0.0-fr, Comité Technique). « Déclaration de symétrie » pour les collectifs symmetric. Lien Atelier des formules. Filtre gravées.

### `/textes/[slug]` — Document vivant

_Clauses versionnées, boucle vote⇒texte fermée_

Sections repliables + scroll-spy. Chaque clause : code, titre, pastille d'inertie CÂBLÉE au protocole d'amendement, mini-jauge sur session réelle, statut. Tap → détail : version courante, propositions avec diff (contenu d'abord, auteur replié), décision fondatrice et amendements chaînés. « Proposer une version » → /decider?clause= (R3, inertie de la clause). Vue projetée conservée. InertiaSlider pédagogique → Atelier pré-réglé. Provenance/GenesisBlock (sources, voteRecord 97/23/19, notes — typos signalées jamais corrigées). Spécifique Pacte : clauses settingKey en clair (« Petit groupe : jusqu'à 5 personnes », « Matière exigée : décisions engageantes et structurantes »), effet IMMÉDIAT à l'adoption (resolveSettings), clause d'inertie protégée par l'inertie maximale ; clause A1 — Notre finalité en tête ; préambule Autonomie–Équilibre–Liaison.

### `/textes/formules` — Atelier des formules

_La pédagogie du seuil — seule maison des lettres_

FormulaEditor : sliders M/B/G/C (+Smith/ComTech), KaTeX de la formule inertielle, table de participation, presets d'inertie comparés. Branché sur engine/threshold.ts UNIQUE. Accessible depuis chaque seuil affiché, pré-réglé (« pourquoi 94 ? »). Cas Forgeron 2026 en un clic (W=7224, T=120 ⇒ 94). Le binaire présenté comme outil d'exception (« l'outil de dernier recours des très grands corps »). Section « Réglage collectif » : la médiane basse par curseur expliquée (robuste aux extrêmes, une position que chacun aurait pu proposer), mini-démo interactive, et la règle de cristallisation (« l'agrégat éclaire, le geste cristallise »).

### `/mandats` — Mandats

_La carte des pouvoirs confiés : qui peut décider quoi, jusqu'à quand_

Cartes par statut (proposé/actif/expiré/révoqué) : titulaire, domaine (cercles + tags), échéance, prochain rapport dû, COMPTEURS BRUTS (« 12 décisions tracées · 1 objection · 2/2 rapports ») — AUCUNE jauge, aucun agrégat. Bouton « Réclamer un mandat » → wizard (souvent pré-rempli par R6).

### `/mandats/[id]` — Fiche mandat

_Le pouvoir confié, visible et reprenable_

Titulaire, origine (la décision créatrice — cliquable), domaine explicite, période, cercle électeur. Bloc « EXERCICE DU MANDAT » : faits comptés jamais synthétisés — n décisions tracées, objections ouvertes/intégrées/escaladées (liste cliquable), rapports rendus/dus. Bloc « FEUX DE LA RAMPE » (sous-titre « toutes les décisions prises sous ce mandat — contestables ») : flux des décisions sous mandat, chacune avec sa fenêtre d'objection. Rapports de redevabilité. « Demander la révocation » en un geste : /decider chaîné (revocation) au protocole de nomination D'ORIGINE, cercle électeur comme périmètre — jamais improvisée, jamais plus coûteuse que l'élection.

### `/mandats/nouveau` — Wizard mandat

_Le meilleur flux v1, conservé et branché sur le pivot_

3 étapes : (1) rôle et domaine (titre, cercles, tags, mission en une phrase) ; (2) modalité de nomination parmi 6 (auto-désignation ratifiée, élection sans candidat — avec sa règle de clôture affichée : pluralité, blanc, départage par vous, jamais par l'outil —, nuancé, consentement, tirage au sort, rotation) avec pros/cons pédagogiques ; (3) durée bornée + cadence de rapports + cercle électeur. Sortie : une Decision createsMandate routée collective — la nomination EST une décision.

### `/donnees` — Données locales

_Local-first assumé : tes données, ton fichier, ta machine_

Export bundle JSON (schemaVersion 2), import avec validation (protocole Consentement requis, collisions) + pose de lineage (« essaimé de … »), réinitialisation par collectif. Seeds (Duniter Ğ1, Atelier du Canal). Identité locale (displayName, isMe) + MES ATTRIBUTS (Person.attributes, clés libres : 'heures/mois'… — « servent uniquement ta carte Pour moi, jamais montrés aux autres »). MODE ATELIER (ex-« agir en tant que ») : saisie assistée en présence — chaque saisie porte « saisi par X pour Y — atelier du <date> » ; l'étiquette démo reste pour les personnages seedés. Niveaux de preuve honnêtes (empreinte locale / IPFS / chaîne — jamais affichés tant que non réels). Note synchro : « un serveur pourra se brancher plus tard, tes données ne changent pas de forme ».

## Vocabulaire UI

| Contexte | Terme final | Remplace |
|---|---|---|
| Carte centrale de /decider et titre du tunnel — le type TS Verdict reste en code ; « verdict » et « triage » ne sortent jamais du code | Le chemin | Le Verdict |
| Popover/panneau Périmètre — pastille pleine ; « la machine t'a désigné » est banni | Concerné·e en premier lieu | calculé (glyphe calculator) |
| Popover/panneau — la main levée reste ; « Ça me concerne » inchangé | Concerné·e en second lieu — s'est déclaré·e | déclaré |
| Chip du tunnel /decider | Concernés | Touchés |
| Panneau de la fiche + frise du chemin ; « Cercle » = groupe durable seulement ; « scope » reste code | Périmètre | Cercles (panneau) / « qui est convoqué » |
| Sidebar + rôle de /mandats/[id] ; tout dérivé de « déléguer » banni de l'UI (test anti-lexique) | Mandats — les pouvoirs confiés | les pouvoirs délégués |
| Header du Fil, compteur nu — interdits : dette, retard, en souffrance ; « dette décisionnelle » survit en métrique interne dev | À toi de décider | dette décisionnelle |
| Fiche et cartes mandat — faits comptés, jamais synthétisés ; « confiance » banni des agrégats | Exercice du mandat | Jauge de confiance |
| Flux public des décisions prises sous un mandat — contestables | Feux de la rampe | Observatoire (bloc fiche mandat) |
| /decisions/observatoire — « comment nous décidons, dans les faits » | L'Observatoire | Mémoire |
| Salle et fiche ; W/T/M/B/G/C vivent uniquement à l'Atelier des formules | Qui vote : 11 personnes — liste arrêtée le 12/08 | corpus visible : W, T / snapshot |
| Échelle nuancée — valeurs 0-5 et commentaire obligatoire 0-1 inchangés, résultats Ğ1 intacts | Pas du tout · Plutôt non · Réservé · Plutôt oui · Oui · Tout à fait | 0=CONTRE … 5=TOUT À FAIT |
| Bloc revoyure — fiche et Fil | L'épreuve du réel / « Le réel a-t-il suivi ? » | Verdict de réalité |
| 3 boutons de l'épreuve du réel (Review.verdict inchangé en code) | Ça tient / À revoir / À révoquer | confirmée / réviser / révoquer |
| Toggle du tunnel + badge ; « conservatoire » survit dans le PV et la doc | C'est urgent / « Décidé en urgence — le collectif ratifie » | décision conservatoire |
| Protocole et mode de la salle — « raisonner curseur / table de mixage » | Réglage collectif / décider au curseur | —(nouveau : VoteMethod 'parametric') |
| Clôture paramétrique : « Les votes sont figés — cristallise la médiane » ; « l'agrégat éclaire, le geste cristallise » — jamais automatique | Cristalliser | —(nouveau : geste du garant) |
| Bandeau de la salle/fiche entre closesAt et le geste du garant | Votes figés — en attente de cristallisation | —(nouveau : status 'frozen') |
| Découpage d'un dossier en micro-décisions — le nom de l'auteur réservé à CE mode (Taïwan) | Décision incrémentielle | —(nouveau : chainKind 'element') |
| Section auto-générée du dossier découpé ; publiée par le geste « clore » du garant, devient le PV du dossier | Cartographie de clôture | —(nouveau) |
| Étiquettes du paramétrique : cadre en lecture seule vs part dérivée — le votant sait ce qu'il contrôle | contrainte / calculé | —(nouveau) |
| Carte d'impact (ma position / médiane / statu quo) — « estimation sur données déclarées », jamais de chiffre inventé | Pour moi | —(nouveau) |
| Bloc de tête salle/fiche : résumé compact toujours visible + dépliables, auto-replié aux visites suivantes | S'instruire | —(nouveau, regroupe l'existant) |
| Ligne suggérée du tunnel + bloc fiche/PV — la décision connexe de ressources ; exigée à l'ouverture, jamais au tunnel | Ce que ça engage | —(nouveau : resources) |
| Alternative permanente du tunnel — l'observatoire en un geste | Déjà tranché — je le consigne | —(nouveau : route 'record') |
| Statu quo en en-tête de fiche ; marqueur « actuel » sur chaque curseur | Aujourd'hui : … / Aujourd'hui → Proposé | —(nouveau : baselineNote) |
| Libellés des routes (lexicon.ts) — le chemin est un geste, pas une catégorie ; formes courtes en pills de filtres | Je décide · Je décide, sous mandat · Je transmets · J'écoute, puis je décide · Nous décidons | Solo / Mandat / Transmettre / Avis / Collectif |
| Salle de vote : la chaîne supersedes n'est jamais publique sous un nom ; seul le dernier vote actif apparaît en ballot open | Ton vote est remplaçable — ton historique n'est visible que de toi | re-vote audité — l'historique reste (indéterminé) |
| Élection : vote déposé sans désignation — compte pour la participation, pas pour la désignation | Vote blanc | —(nouveau : élection) |
| Règle d'élection affichée en salle ; départage = session chaînée entre ex æquo ou tirage au sort si le Pacte l'a prévu | En cas d'égalité, vous départagez — jamais l'outil | —(nouveau : outcome 'tie') |
| Fenêtre d'objection non réversible prolongée : ≥1 « Ça me va » requis — le silence ne vaut accord que pour le réversible | Il manque un accord explicite | —(nouveau : Assent) |
| Description seedée du protocole binaire + Atelier ; libellé Ğ1 : « héritage Toile de Confiance » | pour/contre — « l'outil de dernier recours des très grands corps » | vote binaire (neutre) |
| Première décision proposée — actes collectifs à l'infinitif (règle de ton n°2) | Adopter notre Pacte | Adoptez votre Pacte |
| Section de l'Observatoire — droit à l'erreur, curiosité du chercheur | Révoquées — ce qu'on en a appris | Cimetière des révoquées |
| /donnees — saisie assistée en présence, passerelle hors-écran | mode atelier — « saisi par X pour Y — atelier du <date> » | agir en tant que (pur démo) |
| Étiquette du sandbox Explorer de la salle paramétrique | exploration — ne compte pas | —(nouveau) |
| Métadonnée d'affichage d'un collectif né d'un bundle d'un autre | essaimé de … | —(nouveau : lineage) |
| Pastille pause de l'objection de frontière | compte à rebours suspendu — frontière contestée | (conservé v1 — il est juste) |

## Seeds

EXTRACTION — script backend/scripts/export_seed_bundle.py (venv existant) : importe seed.py (33 items Certification, 59 Forgeron, GENESIS, VOTER_NAMES ×11, mandates ×3, protocoles) et émet frontend/app/data/seeds/duniter-g1.bundle.json au schéma Bundle v2 (avec assents:[]). TEST DE NON-PERTE (vitest, bloquant) : 33+59 clauses, presets d'inertie conservés, provenance.voteRecord == {for:97, against:23, invalid:19, wotSize:7224, thresholdRequired:97, modeParams:'D30M50B.1G.2S.1'}, 11 personnes dont 5 smiths, 3 mandats, protocoles complets. Diff exigé AVANT dépréciation de seed.py. Typos des textes votés : Provenance.notes, JAMAIS corrigées.

MAPPING Ğ1 (v1 conservé, amendé) : Collective{template:'free-currency', isTransparent:true} ; 11 Person (5 smiths) ; Circles typés kind — « Toile de confiance » (racine), « Forgerons » (team), « Comité Technique » (team) ; TextDocs Certification (33 clauses, presets câblés), Forgeron (59 clauses v2.0.0-fr, voteRecord complet), Comité Technique ; pacte-duniter-g1 avec clauses settingKey (smallGroupMax=5, collectiveMin=50, consentMax=7, fenêtres 48/72, framingDays=14, concernEscalateRatio=0.5, recurrenceThreshold=3, reviewDelayDays=90, requireEffects='binding', clause A1 « Notre finalité » adaptée à la toile), clause d'inertie en preset 'max', description du Pacte « Notre contrat social — sacralisé, jamais immuable ». protocolByRange : consent → « Consentement » (D7, OBLIGATOIRE partout), large → « Vote WoT standard » (binaire inertiel D30M50B.1G.2, description « l'outil de dernier recours des très grands corps — héritage Toile de Confiance ») — L'HÉRITAGE À SA PLACE ; clauseByInertia mappé sur les 4 presets ; + « Réglage collectif » (parametric, D14, parametricMinParticipants), « Vote nuancé » (D30), « Vote forgeron Smith » (S.1), « Vote ComTech » (T.1), « Élection sans candidat » (D14, electionMinParticipants, tieBreak:'runoff') — tous ballot:'open'. Chaque clause courante reçoit sa DÉCISION FONDATRICE. 3 Mandates (ComTech Cgeek active + rapport dû ; Administrateur Forgerons proposed EN VOTE nuancé ; Modération Forum active). Décisions historiques : Runtime Upgrade 1100 (adopted, structural, gravée, revoyure posée + brief.effects rétro-documentés), Évolution licence v0.4.0 (framing avec versions).

DEUXIÈME COLLECTIF — atelier-du-canal.bundle.json (test permanent de marque blanche) : association fictive 12 membres, Pacte à seuils DIFFÉRENTS (smallGroupMax=4, fenêtres courtes, requireEffects='binding'), protocolByRange.large → NUANCÉ (la démonstration anti-binaire), Règlement intérieur 8 clauses, cercles typés Bureau (team) / Ateliers (theme) / Tous (racine), mandat Trésorerie (rapport dû), Person.attributes seedés ('heures/mois'). Décisions à TOUS les états : draft, advice en fenêtre, objection SUSPENDUE par frontière, objection non-easy avec 1 Assent posé (démonstration « accord explicite »), framing avec 2 versions et diff, voting nuancé en cours (histogramme vivant), adopted avec épreuve du réel due (brief.effects + measurerIds + resources posés — boucle complète), revoked (« ce qu'on en a appris »), conservatoire + ratification chaînée, UNE CONSIGNATION 'record' (« Le marché du samedi est reconduit — décidé au café, comme d'habitude ») et UN DOSSIER DÉCOUPÉ (2 éléments dont 1 terminal, priorités posées, cartographie de clôture en construction) ; et LE PARAMÉTRIQUE D'EXEMPLE : « Répartir le budget d'ateliers 2026 » en voting — resources { note:'budget annuel ateliers', amount:1200, unit:'€' }, paramSpec sum100 avec 3 parts nommées + EXACTEMENT 1 part 'derived' « Réserve (calculé) », baselines posées, impactAttrKey 'heures/mois', 5-6 votes seedés → faisceau, médiane basse et « Pour moi » calculables et vivants à l'ouverture ; une SECONDE session paramétrique 'frozen' (closesAt passé) avec la carte « À cristalliser » au Fil du garant — la cristallisation-geste démontrée. Tout contenu Ğ1 en dur dans le code est structurellement impossible.

GABARITS (/creer, app/data/templates.ts) — SEPT : blank « Page blanche — observatoire d'abord » (Pacte : clause consignation + A1, protocole Consentement SEUL — l'invariant protocolByRange.consent satisfait, toutes les autres clés en repli → consent, requireEffects='none') ; informal (requireEffects='none') / association / cooperative / commune réétiquetés « point de départ — tout est amendable par décision », protocolByRange.large → nuancé partout, requireEffects='binding' ; free-currency (mapping Ğ1, large → binaire inertiel, unité DU par défaut dans resources) ; symmetric « Institution symétrique » (Pacte transparence totale, TextDoc « Déclaration de symétrie », première décision « Déclarer notre symétrie », mandats suggérés Ambassadeur et Négociateur). Chaque gabarit : préambule Autonomie–Équilibre–Liaison proposé et modifiable + clause A1 (défaut : boussoles vitalité/émancipation) + cercles typés kind + protocole Consentement au minimum. Les seeds s'importent par le MÊME chemin que l'import utilisateur ; lineage posé si bundle d'origine étrangère ; l'import valide l'invariant consent.

## Directives de design

ICONOGRAPHIE DES ROUTES (vocabulaire visuel fixe) : solo = i-lucide-zap ; sous mandat = i-lucide-key-round ; transmettre = i-lucide-send ; avis = i-lucide-message-circle ; collectif = i-lucide-users ; consignation = i-lucide-notebook-pen ; urgence = i-lucide-siren ; déjà décidé = i-lucide-book-open-check ; récurrence→mandat = i-lucide-repeat ; réglage collectif = i-lucide-sliders-horizontal ; cristallisation = i-lucide-stamp (le geste). Carte Le chemin : icon-container 14 px, nom de route en 700 À LA PREMIÈRE PERSONNE, l'explication en une phrase dessous (jamais de code R*), frise périmètre/durée/seuil, bouton principal pill 20 px plein accent, alternatives en pills fantômes. Teintes déclinées des custom properties du mood.

PÉRIMÈTRE : stacks d'avatars (initiales, teinte déterministe de l'id), débord -8 px, « +N ». Premier lieu = pastille pleine en coin (glyphe calculator SUPPRIMÉ) ; second lieu = main levée + fond légèrement teinté. Tap → popover chaîne d'inclusion. Plancher verrouillé visuellement (pas de croix de retrait sur les premiers-lieu). Cercles emboîtés en chips imbriquées ; icône par kind (lieu/thème/équipe).

CYCLE DE VIE : timeline verticale à pastilles pleines reliées (pas de bordure) — draft neutre, avis/objection ambre + compte à rebours, vote bleu + mini-jauge, FIGÉ bleu-gris avec icône tampon en attente (« votes figés — en attente de cristallisation »), en vigueur vert, épreuve du réel violette, révoqué terre cuite, clos gris, rejeté barré. SUSPENSION : pastille pause + fond strié + « compte à rebours suspendu — frontière contestée ». Fenêtre prolongée (Assent manquant) : ambre soutenu + « il manque un accord explicite ». Un seul mapping état→couleur.

JAUGE INERTIELLE (signature, héritage) : arc 270°, seuil qui DESCEND quand la participation monte, chiffres clairs, phrase générée sujet-collectif. KaTeX réservé à l'Atelier.

NUANCÉ : 6 segments en gradient sémantique construit depuis la palette du mood (skill dataviz, jamais rouge/vert crus) ; HISTOGRAMME des 6 niveaux sous la jauge (la distribution, pas seulement le résultat) ; commentaire obligatoire inline sous 0-1.

CURSEURS (Réglage collectif) : poignées tactiles ≥ 2.25 rem AVEC alternative clavier + champ numérique (correction SejeteralO) ; libellé métier au-dessus, unité, valeur courante en 700 ; marqueur statu quo = losange creux sur le rail ; part « calculé » = rail estompé non saisissable + étiquette « calculé » ; étiquette « contrainte » sur le cadre ; clamp dynamique visible (le rail se raccourcit quand la réserve dérivée approche de ses bornes). FAISCEAU : strip-plot par curseur — chaque position = point translucide anonyme (opacity ~0.25), médiane = trait épais accent, baseline = marqueur creux ; « transparence des agrégats, discrétion des personnes ». Carte « Pour moi » : fond légèrement teinté accent, trois colonnes ma position / médiane / statu quo, mention « estimation sur données déclarées ». Sandbox Explorer : bandeau « exploration — ne compte pas », curseurs en teinte neutre. CRISTALLISATION : bouton-tampon réservé au garant, confirmation avec rappel de bimodalité le cas échéant — le geste est solennel mais léger (un tap + confirmation), jamais une cérémonie.

MICRO-CÉLÉBRATION D'ADOPTION : le tampon 井 incliné -10° s'appose « Décidé » (translateY du système, 0.12s ease, aucune fanfare) + « C'est décidé — et révisable, comme tout ici ».

GRAVURE ET PREUVE : badge 井 tampon incliné sur objets gravés ; fiche de preuve sha256 mono tronqué copiable + « empreinte locale — démo ». Sceau hexagramme #48 Tsing : géométrie SVG canonique (viewBox 112×105, 6 traits, trait 1 en bas = dernier rect, opacity ~0.28, drop-shadow CSS), fixe bas-droite, jamais recouvert ; FAB au-dessus avec décalage.

PRINT A4 : feuille CSS print pour le PV (/decisions/[id]) et l'affiche de session (/decisions/[id]/vote) — titre, texte, qui vote, seuil, échéance, comment participer ; noir sur blanc, sans moods. La cartographie de clôture d'un dossier s'imprime comme PV.

MOODS v2 (champ lexical du puits, 2 light + 2 dark) : Source (light bleu d'eau), Margelle (light sable/pierre), Nappe (dark bleu profond), Minuit (dark encre). Socle : borderless (background + box-shadow), arrondis 16/12/20/14, hover cards translateY(-3px) 0.12s, mobile-first clamp(), breakpoints 480/768/1024, touch ≥ 2.25 rem. useMood.ts conservé.

## Hors scope v2

- Décision paramétrique COMPLÈTE : équation de réalité pluggable (viabilité auto-calculée type p0 non linéaire), éditeur de courbes 'curve' (Bézier, ParamKind réservé), impact sur données réelles importées du périmètre, rétro-manipulation résultat→contrainte — v2 livre curseurs bornés + répartition sum100 (un derived, clamp/renormalisation spécifiés) + médiane basse + faisceau + baseline + impact linear-share sur attribut déclaré ; le solveur et le registre importé en v3
- Builder d'indicateurs de l'observatoire (indicateurs propres définis par le collectif) — construire l'éditeur avant d'avoir observé la pratique contredirait l'observatoire lui-même ; v2 = stats fixes + effets recherchés/constatés + consignations
- Vecteur topologique calculé (bassin de vie, commune pivot, géo) et règles d'appartenance WoT — v2 : listes nominatives + Circle.kind déclaratif ; enum de règle extensible sans migration
- Vote secret cryptographique et pseudonymat certifié @personne (deux identités découplées) — v2 : ballot 'secret' en secret d'affichage honnête + dépersonnalisation d'ordre d'affichage ; la crypto arrive avec la synchro et l'auth Duniter
- Économie du don outillée : allocation en DU effective, budget de gratitude, caisses fléchées, coefficients relatifs, rétribution des mandats pilotée par le collectif — v2 se limite à l'unité 'DU' descriptive dans resources (gabarit free-currency) ; l'intégration exige Ğ1/GrateWizard
- Grappes et fédération inter-collectifs opérationnelles — v2 pose lineage + le pattern documenté « une grappe = un collectif ordinaire dont les membres sont des mandataires d'autres collectifs ; la fédération = échange de bundles, jamais une table » ; jamais de parentCollectiveId
- Engagements fermes à seuil critique (crowdaction : recueillir, ne déclencher qu'au quota) — nouveau type d'objet et de cycle de vie, backlog v3
- Cartographie thématique vectorielle (liaisons, interdépendances, symptômes/causes visualisés) — v2 : tags plats + champ symptomsVsCauses du brief ; la carte manipulable est un produit en soi
- Événements de brassage générés par l'outil et multi-postes simultanés pour ateliers physiques — v2 livre l'imprimable A4 + le mode atelier mono-poste (intermédiation humaine, doctrinalement acceptable)
- Délégation liquide / procuration — si elle arrive en v3, la nommer « mandater son vote » (jamais « déléguer » — le test anti-lexique l'attrapera)
- IA repositionnée sur la recherche des biais décisionnels (seul usage autorisé par la doctrine) — exige corpus réel + backend ; jamais de suggestion prescriptive
- Synchronisation backend / adaptateur FastAPI — contrat des stores et Bundle prêts (invariants privacy des re-votes et ballot secret notés au contrat) ; brancher l'API ne change aucun écran
- Auth Duniter challenge-réponse Ed25519 — identités locales v2, duniterAddress réservé
- Notifications sortantes (email, digest) et WebSocket temps réel — tout est in-app dans le Fil ; silence-vaut-accord réservé au réversible en conséquence, et l'Assent couvre le reste
- IPFS et ancrage on-chain — proofLevel 'local' uniquement, champs réservés jamais affichés
- Jugement majoritaire, catalogue 18 modalités × 9 nominations, ProcessTemplate opérationnels, exports juridiques signés — bibliothèque documentaire ; le PV markdown/A4 reste « registre de légitimité, jamais substitut légal »
- Passerelles hors-écran complètes (relais humains institués, restitutions événementielles) et horizon des institutions symétriques réelles — consignés dans la page-manifeste/README comme horizon nommé, pas comme dette

## Plan d'implémentation

1. 1. SOCLE, LEXIQUE ET MOTEURS — branche v2, shell vers layouts/, moods 4 ambiances, types/domain.ts complet (ci-dessus, avec Assent, frozen, choicePersonId), app/lexicon.ts (source unique + test vitest anti-lexique + 3 règles de ton) DÈS CE SPRINT, engine/ TS pur (threshold avec electionResult, nuanced, parametric avec crystallize, modeParams, settings avec replis, triage, state avec canTransition(decision,to,ctx), impact) avec vitest portant test_threshold/test_qualifier/test_nuanced/test_mode_params + specs parametric (médiane BASSE step-safe ; sum100 exactement-un-derived + clamp/renormalisation ; computeMyImpact linear-share ; detectBimodality ; dégénérescences 0-vote) + specs election (pluralité, blanc, quorum, tie) + specs state (Assent, affluence, matière, dossier) — le cas Forgeron W=7224 T=120 ⇒ 94 vert AVANT tout écran
2. 2. PERSISTANCE ET SEEDS — data/persistence.ts (IndexedDB, export/import, validation consent obligatoire, lineage à l'import), export_seed_bundle.py, bundles duniter-g1 + atelier-du-canal (paramétrique d'exemple calculable avec resources.amount, session frozen à cristalliser, consignation, dossier découpé, attributs, Assent démontré), tests de non-perte bloquants (33/59 clauses, 97/23/19/7224, 11 personnes, 3 mandats, protocoles), templates.ts 7 gabarits (blank en tête avec Consentement seul, large→nuancé partout sauf free-currency, requireEffects par gabarit)
3. 3. SHELL ET ONBOARDING — header (WorkspaceSelector + finalité A1, Cmd+K, MoodSwitcher, Décider), FAB, sceau 井 canonique, /creer 3 écrans (phrase du rêve, 7 gabarits, « Adopter notre Pacte », signature « Osons, nous verrons »), /donnees (export/import/reset, identité, attributs, mode atelier)
4. 4. LE GESTE — /decider complet (Q0 inline, 3 chips, ligne « Ce que ça engage » suggérée jamais bloquante, baselineNote suggéré, carte Le chemin sans code de règle, C'est urgent, dérogation asymétrique, alternatives permanentes réglage/consignation/binaire-avec-coût, suggestions R6+maturation, création structurée) + / Aujourd'hui (capture sticky, Fil sélecteurs purs incluant cartes À cristalliser / Dossier complet / accord explicite manquant, « À toi de décider » compteur nu, boutons inline) — MESURER : 2 gestes < 10 s, et la consignation aussi
5. 5. CYCLE DE VIE — /decisions (registre) + /decisions/[id] (timeline avec état figé, panneau Périmètre premier/second lieu auditable, Ça me concerne, objection de frontière suspensive, garde d'affluence non-ignorable + scopeKeptNote, bloc S'instruire condensé auto-repliable, fenêtres : silence-vaut-accord easy seulement + Assent + prolongation, gardes d'ouverture matière/ressources, chaînage, Remettre en question au protocole d'origine, PV + print A4, gravure honnête)
6. 6. LE VOTE — /decisions/[id]/vote : consentement (Assent stocké), nuancé (libellés définitifs, histogramme, commentaire 0-1), binaire hérité (jauge inertielle, phrase sujet-collectif, lien Atelier), PARAMÉTRIQUE (curseurs + clavier, statu quo, contrainte/calculé, clamp dynamique, faisceau, médiane basse expliquée, Pour moi, Explorer, frozen + geste Cristalliser du garant + quorum + bimodalité), ÉLECTION (désignation, blanc, pluralité, tie → départage humain), ballot secret d'affichage, liste arrêtée, re-vote à historique privé, mode atelier (recordedById), affiche A4, adoption ⇒ application automatique + micro-célébration 井
7. 7. LES TEXTES — /textes + document vivant (clauses, versions/diff auteur replié, vue projetée, inertie câblée, Provenance/GenesisBlock, Pacte effectif : resolveSettings lu par le triage avec replis, A1 finalité, préambule boussoles, sous-titre, clause requireEffects en clair) + /textes/formules (FormulaEditor sur moteur unique + section médiane/cristallisation du Réglage collectif + binaire repositionné en outil d'exception)
8. 8. LES MANDATS — /mandats (compteurs bruts, AUCUNE jauge), fiche (Exercice du mandat, Feux de la rampe, rapports, révocation chaînée au protocole d'origine), wizard 3 étapes pré-rempli par R6 (règle d'élection affichée)
9. 9. L'OBSERVATOIRE ET LA BOUCLE APPRENANTE — /decisions/observatoire (stats + consigné/outillé, ressources par unité huile/eau, épreuves du réel, Révoquées — ce qu'on en a appris, élagage), l'épreuve du réel complète (tableau recherché/constaté, measurerIds saisissent), suggestions du Fil (récurrence⇒mandat, maturation⇒protocolisation, sunset, élagage) — le système se simplifie lui-même
10. 10. DÉCISION INCRÉMENTIELLE — découpage en micro-décisions (chainKind element), onglet Éléments, pondération des enjeux (Concern.priority), cartographie de clôture auto-générée, clôture du dossier par le geste du garant (framing→closed gardée) — livrée après le socle car elle compose des primitives déjà construites
11. 11. FINITION LIVRABLE — QA mobile+desktop zéro régression (480/768/1024, touch 2.25 rem), parcours complets sur les 2 seeds ET un collectif Page blanche (« Adopter notre Pacte » < 10 min avec un non-théoricien ; consigner→observer→protocoliser déroulé ; cristallisation et départage d'élection déroulés), test anti-lexique vert, a11y clavier (curseurs inclus)/contraste, print A4 vérifié, README + CONTRIBUTING + page-manifeste publique (sans vocabulaire de livraison, close sur « La ville change, le puits demeure »), npm run build zéro erreur, screenshots
