feat(landing): mutation racontée + état 3 régimes + SVG boucle
Trois améliorations issues de l'audit ChatGPT du manifeste : 1. Nouvelle section 04 "une mutation racontée" — récit factuel de prec_013 (rls_policy_with_app_context) en timeline 6 étapes : proposition silencieuse → détection manifest → arbitrage Polis → précédent inscrit → manifest mis à jour → refus runtime du prochain agent. Rend l'autocatalyse tangible sur un cas réel. 2. Section 07 "état" refondue en 3 colonnes (au lieu de 2) : - Runtime live et vérifié - Doctrine ratifiée, runtime partiel - En écriture Évite la lecture "tout est déjà fermé" sur la section État. Aligne avec la doctrine lazy (S173k+11). 3. SVG diagramme de la boucle PM → Claude → Cowork → MCP externe ajouté dans la section 02, plus parlant que les nœuds décoratifs du hero. Corrections compteurs alignées sur counter-sync : 67→68 helpers, 162→163 migrations, 14→15 tests adversariaux. Renumérotation des sections (4-5-6-7 au lieu de 4-5-6). RWD vérifié : 6 breakpoints couvrant 480/560/640/720/880/960px. Timeline marker + svg + state-grid-3 ajoutés à mobile.
This commit is contained in:
parent
1f253b6c4b
commit
8bd91d9f26
161
index.html
161
index.html
@ -124,6 +124,52 @@
|
||||
Chaque tour de boucle laisse l'habitat un peu plus précis, un peu plus défendu, un peu plus capable de se gouverner seul.
|
||||
</p>
|
||||
|
||||
<figure class="loop-svg-figure" aria-hidden="true">
|
||||
<svg viewBox="0 0 720 360" class="loop-svg" preserveAspectRatio="xMidYMid meet">
|
||||
<defs>
|
||||
<marker id="arrowhead" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#7eecda"/>
|
||||
</marker>
|
||||
<radialGradient id="loopNode" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#111114"/>
|
||||
<stop offset="100%" stop-color="#0a0a0c"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Nodes (4 — placed in a diamond) -->
|
||||
<g class="loop-svg-node">
|
||||
<circle cx="120" cy="180" r="58" fill="url(#loopNode)" stroke="#2a4a45" stroke-width="1.5"/>
|
||||
<text x="120" y="172" text-anchor="middle" class="loop-svg-tag">PM BOARD</text>
|
||||
<text x="120" y="192" text-anchor="middle" class="loop-svg-label">décide</text>
|
||||
</g>
|
||||
<g class="loop-svg-node">
|
||||
<circle cx="360" cy="70" r="58" fill="url(#loopNode)" stroke="#2a4a45" stroke-width="1.5"/>
|
||||
<text x="360" y="62" text-anchor="middle" class="loop-svg-tag">CLAUDE CODE</text>
|
||||
<text x="360" y="82" text-anchor="middle" class="loop-svg-label">écrit</text>
|
||||
</g>
|
||||
<g class="loop-svg-node">
|
||||
<circle cx="600" cy="180" r="58" fill="url(#loopNode)" stroke="#2a4a45" stroke-width="1.5"/>
|
||||
<text x="600" y="172" text-anchor="middle" class="loop-svg-tag">COWORK</text>
|
||||
<text x="600" y="192" text-anchor="middle" class="loop-svg-label">observe</text>
|
||||
</g>
|
||||
<g class="loop-svg-node loop-svg-ext">
|
||||
<circle cx="360" cy="290" r="58" fill="url(#loopNode)" stroke="#7eecda" stroke-width="1.5"/>
|
||||
<text x="360" y="282" text-anchor="middle" class="loop-svg-tag">MCP EXTERNE</text>
|
||||
<text x="360" y="302" text-anchor="middle" class="loop-svg-label">contre-propose</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrows (curved between adjacent nodes) -->
|
||||
<path d="M 168 145 Q 240 80 305 80" fill="none" stroke="#7eecda" stroke-width="1.5" marker-end="url(#arrowhead)" opacity="0.8"/>
|
||||
<path d="M 415 80 Q 480 80 552 145" fill="none" stroke="#7eecda" stroke-width="1.5" marker-end="url(#arrowhead)" opacity="0.8"/>
|
||||
<path d="M 552 215 Q 480 280 415 290" fill="none" stroke="#7eecda" stroke-width="1.5" marker-end="url(#arrowhead)" opacity="0.8"/>
|
||||
<path d="M 305 290 Q 240 280 168 215" fill="none" stroke="#7eecda" stroke-width="1.5" marker-end="url(#arrowhead)" opacity="0.8"/>
|
||||
|
||||
<!-- Center label -->
|
||||
<text x="360" y="178" text-anchor="middle" class="loop-svg-center">autocatalyse</text>
|
||||
<text x="360" y="198" text-anchor="middle" class="loop-svg-center-sub">chaque mutation laisse un précédent</text>
|
||||
</svg>
|
||||
</figure>
|
||||
|
||||
<div class="loop-grid">
|
||||
<div class="loop-node">
|
||||
<div class="loop-tag">PM Board</div>
|
||||
@ -207,9 +253,80 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ═══════════════════ MUTATION RACONTÉE ═══════════════════ -->
|
||||
<section id="mutation" class="block dark-block">
|
||||
<p class="section-num">04 — une mutation racontée</p>
|
||||
<h2>La machine apprend une règle. Le prochain agent ne peut plus l'ignorer.</h2>
|
||||
<p class="block-lede">
|
||||
Récit factuel d'un précédent ratifié : <code>prec_013</code>. Cinq mois plus tôt, n'importe quel agent pouvait ouvrir une connexion sur une table multi-tenant. Aujourd'hui, le runtime refuse — pas un reviewer, pas un linter : la base elle-même.
|
||||
</p>
|
||||
|
||||
<ol class="timeline">
|
||||
|
||||
<li class="timeline-step">
|
||||
<div class="timeline-marker">i</div>
|
||||
<div class="timeline-body">
|
||||
<div class="timeline-tag">proposition</div>
|
||||
<h3>Un agent veut écrire dans une table multi-tenant.</h3>
|
||||
<p>La table porte du RLS <code>FORCE</code>. La policy existe, le rôle Postgres ne possède pas <code>BYPASSRLS</code>. Premier réflexe d'agent : ouvrir <code>postgres.js</code>, lancer l'<code>INSERT</code>. Le résultat n'est pas une erreur. C'est <em>silence</em> : zéro ligne écrite, zéro exception, zéro indication. Le bug le plus dangereux est celui qui ne crie pas.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="timeline-step">
|
||||
<div class="timeline-marker">ii</div>
|
||||
<div class="timeline-body">
|
||||
<div class="timeline-tag">détection</div>
|
||||
<h3>Le manifest exige une catégorie d'enforcement.</h3>
|
||||
<p>Toute mutation déclarée doit choisir parmi cinq catégories ratifiées. L'agent tente <code>rls_policy</code> seul. Le gate d'admission refuse : ce mécanisme n'est pas dans la liste des dix <code>equivalent_enforcement</code> fermés. La doctrine n'admet pas un mécanisme par convenance.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="timeline-step">
|
||||
<div class="timeline-marker">iii</div>
|
||||
<div class="timeline-body">
|
||||
<div class="timeline-tag">arbitrage polis</div>
|
||||
<h3>L'architecte tranche, conditions explicites.</h3>
|
||||
<p>Polis examine : RLS protège la ligne, mais sans contexte applicatif posé (<code>app.org_id</code>, <code>app.actor_id</code>), la policy retourne <em>vide</em> au lieu d'<em>erreur</em>. Verdict : un mécanisme composé est nécessaire. Cinq conditions cumulatives, six prédicats fermés, audit différencié, test adversariel par surface.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="timeline-step">
|
||||
<div class="timeline-marker">iv</div>
|
||||
<div class="timeline-body">
|
||||
<div class="timeline-tag">précédent inscrit</div>
|
||||
<h3><code>prec_013</code> entre au registre, append-only.</h3>
|
||||
<p>Le mécanisme prend un nom : <code>rls_policy_with_app_context</code>. Il devient le neuvième <code>equivalent_enforcement</code> ratifié. Le précédent est écrit dans <code>polis_decisions</code> avec ses cinq conditions, ses six prédicats, son test adversarial obligatoire. Le runtime peut le citer ; il ne peut pas l'inventer.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="timeline-step">
|
||||
<div class="timeline-marker">v</div>
|
||||
<div class="timeline-body">
|
||||
<div class="timeline-tag">manifest mis à jour</div>
|
||||
<h3>Le contrat devient lisible par la machine.</h3>
|
||||
<p>L'entrée du manifest référence <code>prec_013</code>. Les helpers <code>withOrg()</code>, <code>withAgent()</code>, <code>withSuperAdmin()</code> posent six GUC Postgres sur la transaction. Le code n'a plus à se souvenir : l'oubli est structurellement impossible.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="timeline-step">
|
||||
<div class="timeline-marker">vi</div>
|
||||
<div class="timeline-body">
|
||||
<div class="timeline-tag">le prochain agent</div>
|
||||
<h3>Refusé par le runtime, pas par un humain.</h3>
|
||||
<p>Un nouvel agent — Claude, ChatGPT, un script interne — tente d'écrire sur la même table sans poser le contexte. Le gate d'admission lit <code>prec_013</code>, vérifie les conditions, refuse. La règle n'est plus dans une spec à lire. Elle est dans la base, dans le compilateur, dans le runtime. C'est ça, l'autocatalyse : chaque erreur réparée devient un mur que la machine ne se cogne plus jamais.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ol>
|
||||
|
||||
<p class="timeline-footer">
|
||||
Précédent ratifié le 25 avril 2026. Trente autres précédents suivent la même mécanique : une situation, un arbitrage, un mécanisme nommé, un test, un gate. La constitution n'est pas un texte. C'est un comportement de la machine.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- ═══════════════════ ÉCONOMIE + UGC ═══════════════════ -->
|
||||
<section class="block dark-block">
|
||||
<p class="section-num">04 — économie + contenu</p>
|
||||
<section class="block">
|
||||
<p class="section-num">05 — économie + contenu</p>
|
||||
<h2>Quand le métabolisme s'auto-régule.</h2>
|
||||
|
||||
<div class="two-col">
|
||||
@ -228,7 +345,7 @@
|
||||
|
||||
<!-- ═══════════════════ SOUVERAINETÉ ═══════════════════ -->
|
||||
<section class="block">
|
||||
<p class="section-num">05 — souveraineté</p>
|
||||
<p class="section-num">06 — souveraineté</p>
|
||||
<h2>Aucune dépendance critique qui ne soit substituable.</h2>
|
||||
<div class="sov-grid">
|
||||
<div class="sov-card">
|
||||
@ -260,32 +377,43 @@
|
||||
|
||||
<!-- ═══════════════════ ÉTAT DE LA MACHINE ═══════════════════ -->
|
||||
<section id="etat" class="block dark-block">
|
||||
<p class="section-num">06 — état</p>
|
||||
<h2>Ce qui tourne déjà. Ce qui s'écrit.</h2>
|
||||
<p class="section-num">07 — état</p>
|
||||
<h2>Trois régimes. Pas un seul.</h2>
|
||||
<p class="block-lede">
|
||||
Une partie de la machine tourne en production, vérifiable. Une autre est ratifiée doctrinalement, avec son implémentation runtime partielle ou en cours d'instrumentation. Une troisième est en écriture. Confondre les trois rendrait le tableau soit magique, soit ambitieux. La distinction est ce qui rend la cadence opposable.
|
||||
</p>
|
||||
|
||||
<div class="state-grid-3">
|
||||
|
||||
<div class="state-grid">
|
||||
<div class="state-block">
|
||||
<h3>Livré runtime</h3>
|
||||
<h3>Runtime live et vérifié</h3>
|
||||
<ul class="state-list">
|
||||
<li><span class="num">42</span><span>tools MCP gateés</span></li>
|
||||
<li><span class="num">42</span><span>tools MCP gateés (OAuth 2.1 + RS256 + JWKS publique)</span></li>
|
||||
<li><span class="num">46</span><span>routes Bun / Hono</span></li>
|
||||
<li><span class="num">67</span><span>helpers TypeScript</span></li>
|
||||
<li><span class="num">162</span><span>migrations SQL idempotentes</span></li>
|
||||
<li><span class="num">31</span><span>précédents constitutionnels ratifiés</span></li>
|
||||
<li><span class="num">47</span><span>mécanismes d'enforcement équivalents au manifest</span></li>
|
||||
<li><span class="num">14</span><span>tests adversariaux constitutionnels</span></li>
|
||||
<li><span class="num">68</span><span>helpers TypeScript</span></li>
|
||||
<li><span class="num">163</span><span>migrations SQL idempotentes</span></li>
|
||||
<li><span class="num">7</span><span>orgs vivantes en production</span></li>
|
||||
<li><span class="num">0</span><span>fichier PHP (depuis le 5 avril 2026)</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="state-block">
|
||||
<h3>S'écrit maintenant</h3>
|
||||
<h3>Doctrine ratifiée, runtime partiel</h3>
|
||||
<ul class="state-list">
|
||||
<li><span class="num">31</span><span>précédents constitutionnels ratifiés — registre <code>polis_decisions</code> live, supersedure traçable</span></li>
|
||||
<li><span class="num">47</span><span>mécanismes d'enforcement au manifest — instrumentation en cours sur les surfaces secondaires</span></li>
|
||||
<li><span class="num">15</span><span>tests adversariaux constitutionnels — couverture par mécanisme à finir</span></li>
|
||||
<li><span class="num">9</span><span>conditions cumulatives ratifiées pour <code>rls_policy_with_app_context</code> (verrou étape 9 partiel)</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="state-block">
|
||||
<h3>En écriture</h3>
|
||||
<ul class="state-list-soft">
|
||||
<li>Bascule étape 9 fail-closed sur les surfaces critiques</li>
|
||||
<li>Bascule étape 9 fail-closed sur les surfaces critiques restantes</li>
|
||||
<li>Agent v4 — registre, capacités, execution bindings append-only</li>
|
||||
<li>Economic Kernel v1 — double-entrée, mandats, freeze</li>
|
||||
<li>Mailbox v1 — saga pattern inter-agent avec timeout et causalité</li>
|
||||
<li>Mailbox v1 — saga pattern inter-agent (timeout + causalité)</li>
|
||||
<li>Dockerisation — fin du bare-metal, dix sous-tâches</li>
|
||||
<li>Lifecycle dashboard <code>/me</code> — le citoyen voit son propre métabolisme</li>
|
||||
<li>OAuth 2.1 authorization_code + PKCE — flow claude.ai natif</li>
|
||||
@ -294,6 +422,7 @@
|
||||
<li>Migration <code>founder_root</code> sur hardware token</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="state-footer">
|
||||
|
||||
148
style.css
148
style.css
@ -473,6 +473,144 @@ code {
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
LOOP SVG (figure dans section #loop)
|
||||
═══════════════════════════════════════════════════ */
|
||||
.loop-svg-figure {
|
||||
max-width: 760px;
|
||||
margin: 0 auto 3rem;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
.loop-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
.loop-svg-tag {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.12em;
|
||||
fill: var(--accent);
|
||||
}
|
||||
.loop-svg-label {
|
||||
font-family: var(--serif);
|
||||
font-size: 13px;
|
||||
font-style: italic;
|
||||
fill: var(--fg);
|
||||
}
|
||||
.loop-svg-center {
|
||||
font-family: var(--mono);
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.08em;
|
||||
fill: var(--fg-muted);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.loop-svg-center-sub {
|
||||
font-family: var(--serif);
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
fill: var(--fg-dim);
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.loop-svg-figure { margin-bottom: 2rem; }
|
||||
.loop-svg-tag { font-size: 9px; }
|
||||
.loop-svg-label { font-size: 11px; }
|
||||
.loop-svg-center { font-size: 11px; }
|
||||
.loop-svg-center-sub { font-size: 9px; }
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
TIMELINE (section "mutation racontée")
|
||||
═══════════════════════════════════════════════════ */
|
||||
.timeline {
|
||||
list-style: none;
|
||||
position: relative;
|
||||
padding: 0 0 0 0;
|
||||
margin: 0 auto;
|
||||
max-width: 760px;
|
||||
}
|
||||
.timeline::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 1.35rem;
|
||||
top: 0.5rem;
|
||||
bottom: 0.5rem;
|
||||
width: 1px;
|
||||
background: var(--accent-dim);
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.timeline::before { left: 1.1rem; }
|
||||
}
|
||||
|
||||
.timeline-step {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 3rem 1fr;
|
||||
gap: 1.25rem;
|
||||
padding: 0 0 2.25rem 0;
|
||||
}
|
||||
.timeline-step:last-child { padding-bottom: 0; }
|
||||
@media (max-width: 560px) {
|
||||
.timeline-step { grid-template-columns: 2.5rem 1fr; gap: 1rem; padding-bottom: 1.75rem; }
|
||||
}
|
||||
|
||||
.timeline-marker {
|
||||
width: 2.7rem;
|
||||
height: 2.7rem;
|
||||
border-radius: 50%;
|
||||
background: var(--bg);
|
||||
border: 1.5px solid var(--accent-dim);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--mono);
|
||||
font-size: 0.9rem;
|
||||
color: var(--accent);
|
||||
letter-spacing: 0.02em;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.timeline-marker { width: 2.2rem; height: 2.2rem; font-size: 0.8rem; }
|
||||
}
|
||||
|
||||
.timeline-tag {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.timeline-body h3 {
|
||||
font-family: var(--serif);
|
||||
font-weight: 400;
|
||||
font-size: clamp(1.15rem, 1.7vw, 1.4rem);
|
||||
line-height: 1.3;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--fg);
|
||||
margin-bottom: 0.75rem;
|
||||
text-wrap: balance;
|
||||
}
|
||||
.timeline-body p {
|
||||
font-size: 0.98rem;
|
||||
color: var(--fg-muted);
|
||||
line-height: 1.65;
|
||||
max-width: 42em;
|
||||
}
|
||||
.timeline-body p em { color: var(--accent); font-style: italic; }
|
||||
|
||||
.timeline-footer {
|
||||
margin: 2.5rem auto 0;
|
||||
max-width: 760px;
|
||||
font-size: 0.95rem;
|
||||
color: var(--fg-muted);
|
||||
font-style: italic;
|
||||
border-top: 1px solid var(--border);
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
STATE
|
||||
═══════════════════════════════════════════════════ */
|
||||
@ -482,8 +620,18 @@ code {
|
||||
gap: clamp(2rem, 5vw, 4rem);
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
.state-grid-3 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: clamp(1.75rem, 3.5vw, 2.75rem);
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
@media (max-width: 960px) {
|
||||
.state-grid-3 { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.state-grid { grid-template-columns: 1fr; }
|
||||
.state-grid-3 { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.state-block h3 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user