---
title: "Lire sans écrire : l'hygiène agentique de Fil"
subtitle: "Masquer n'est pas supprimer, replier n'est pas cacher, et un score sans raison n'est pas un score"
date: 2026-09-30
time: "20:00"
author: VFG / Fil (Chronowflow)
publishedBy: code
status: pub
tags: ["fil", "chronowflow", "devblog", "hygiene", "privacy", "agents", "invariants"]
---

Les agents de coding écrivent vite. Les ledgers, eux, doivent rester **append-only** et honnêtes. Fil (Chronowflow) s'est construit autour d'une contrainte simple : **il lit Chronos, il ne le mute jamais**. Ce billet détaille cette hygiène — privacy bornée, vues locales, scores expliqués — et pourquoi elle protège à la fois l'humain et les agents suivants.

## Le contrat en une ligne

> **Fil lit, il n'écrit pas.** Chronos est le seul store durable de la chaîne captation. Fil ne réinjecte rien dans le ledger.

Tout le reste — masquer, concentrer, plafonner, journaliser — est une **vue** ou un **dérivé**. Pas une réécriture de l'histoire.

## Masquer ≠ supprimer

| Action UI | Effet réel | Persistance |
| --- | --- | --- |
| Dismiss agent (X) | Vue locale `fil.agent.dismiss.v1` | Navigateur / workspace Fil |
| « Supprimer tout » | Marqueur `cleared` + purge vue | Empêche la résurrection démo ; **ne touche pas** Chronos |
| Hide terminal | Après `finished` / `failed` / `interrupted` + rétention | Toujours vue Fil |
| Silence non-terminal | Passe `stale` **visible** | Voulu : ne pas faire disparaître un agent muet |

Conséquence pour un POC de présence : si la session n'apparaît pas dans Fil alors que Chronos l'a ingérée, vérifier d'abord un **dismiss local** — le serveur ne peut pas le savoir.

## Replier ≠ masquer

Le journal et Flux **replient** `noise` / `ambient` pour que le bilan de travail soit lisible. Mais :

- les compteurs `excluded` / `byCategory` restent exposés ;
- la ligne **budget** (surplus par source) est épinglée et **exemptée** du filtre catégorie par défaut ;
- un plafond Top-N sans compteur visible serait un mensonge d'interface.

Mesure citée (session captation) : journée « 141 / 68 / dispersion 10 » → après repli « **4 items de travail**, 137 repliés, 3 projets, dispersion 3 ». Rien n'a été détruit.

## Le bruit n'est pas une poubelle

Changement de comportement majeur du plan captation : **plus rien n'est détruit à la captation**. Un acte bruyant devient `category: "noise"`, tagué, plafonné (ex. ≤ 18/100), dé-priorisé. Mesure : **201** items auparavant jetés, conservés sur un échantillon réel.

Pourquoi c'est de l'hygiène agentique : un agent suivant peut auditer *pourquoi* la journée était bruyante. Un hard-drop rendait cette preuve impossible.

## Privacy bornée (`sanitizeFlowPayload`)

Non conservés / non rendus dans les payloads de flux :

- prompt intégral
- diff
- presse-papiers
- corps de message (`message_content`)
- keystrokes / screenshot / `raw_url` (liste STATUS captation)

Titres : **120 caractères max**.

Git : **`git log` seul** — jamais le contenu. La racine du dépôt est exigée pour éviter l'attribution parent (bogue réel corrigé en WP3 : `public/` renvoyait 11 commits du parent).

**Aucune donnée fabriquée** si une source est absente ou illisible.

## Un score s'explique

Contrat produit :

- chaque score porte des **`reasons[]`** ;
- une raison de **pénalité** n'est **jamais tronquée** et passe en tête (`reasonLine`) ;
- la pénalité `hors-focus` n'est **jamais masquante** — elle explique, elle ne censure pas.

Preuve navigateur WP8 : focus actif → score **50 → 40** avec `hors-focus` **en tête de ligne**.

Hypothèse non mesurée ici : le ressenti humain « est-ce que je comprends assez vite ? » — non instrumenté ; seuls les garde-fous techniques sont prouvés.

## Présence fail-open

Si VFGate ou Chronos est indisponible :

1. **Signaler** (bandeau santé, source `down`).
2. **Continuer** (autres sources, UI utilisable).

Un `200 ok:true` VFGate **ne prouve pas** une livraison (`mock-only` / `no-adapter` / erreur avalée restent possibles). Fil observe la présence via Chronos ; il ne « valide » pas le hub.

## Pureté et tests : l'hygiène du code

| Zone | Règle |
| --- | --- |
| `src/lib/flows/**` | Pur : pas de réseau, pas de React, pas de global navigateur |
| `flows/server/**` | Seule zone I/O de collecte |
| Script `npm test` | **Liste explicite** de fichiers — un test oublié **ne tourne jamais** (piège silencieux) |
| Lint | Pas de `Date.now()` dans un `useMemo` ; pas de `setState` dans le corps d'un effet |

Un agent qui ajoute un fichier de test sans l'inscrire dans `package.json` croit avoir prouvé quelque chose : il n'a rien prouvé.

## Ce que cette hygiène refuse aux agents

- Muter le ledger Chronos « pour nettoyer ».
- Inventer des commits, des minutes actives, ou un journal « complet » parce que la page est courte (pagination Chronos **partielle** : un journal court **ne prouve pas** l'absence de travail).
- Coller un secret, un token, ou un prompt intégral dans un titre, un log, un commit ou un handoff.
- Présenter une source `declared` comme live.

## Preuves vs non exécuté

### Preuve (documentée STATUS / DERNIERE-INFO / WP)

- Invariants formalisés dans `AGENTS.md`.
- Privacy et non-fabrication couvertes par la suite captation (compteurs historiques **185** puis **197** puis **213** puis **228** puis **246** tests selon les tranches WP).
- Navigateur WP8 : cohérence journal, pénalité visible, fail-open JS.

### Non exécuté dans ce billet

- Audit privacy exhaustif de *toutes* les routes hors Flux.
- Preuve que zéro prompt fuit jamais via un autre chemin UI (hypothèse de couverture : centrée `sanitizeFlowPayload` + adaptateurs Flux).
- Rejeu de la suite au moment de la rédaction.

## Une phrase pour la suite

L'hygiène de Fil, c'est de laisser Chronos être la mémoire et Fil être le **regard** — un regard qui peut se tromper d'interprétation, mais qui n'a pas le droit de réécrire ni d'inventer.

---

*Sources : `AGENTS.md`, `STATUS.md`, `DERNIERE-INFO.md`, handoffs captation WP0–WP8, `docs/DEV-ENVIRONNEMENT.md`. Miroir : `Chronowflow/docs/devblog/`.*