Ce commit porte deux sessions à la fois — voir la note de collision en fin de message. C4 — compression des images à l'upload, flutter build web vert. Un seul helper appelé par les deux chemins d'upload de resources_screen, le motif exact qui a fait diverger Create et Upload côté serveur en C1 puis en C3. La compression se fait avant resourceCreate parce que le pré-vol de quota de C3 porte sur sizeBytes à la création. Écart assumé à l'énoncé « JPEG q82 » : un PNG à canal alpha reste un PNG, sinon la transparence se remplit de noir. Défaut préexistant trouvé au passage : le second chemin d'upload ne renseignait sizeBytes ni avant ni après, donc tout ce qui est passé par là compte 0 octet au quota. K8 — retour à l'accueil après 5 min, flutter build apk vert. Écrit une seule fois dans section_page_detail, qui enveloppe les treize types. Le retour passe par le même dispose, donc il émet un sectionLeave avec sa durée réelle : K8 répare une statistique en plus d'un écran. Checklist D0 ajoutée — DOCS/claude design/checklist-d0-offline.html. Les 15 cas du §21, chaque groupe relié au bug qu'il décide : 21.2 tranche D2 et D4, 21.1.4 tranche D3, 21.4.1 tranche D5. Les cas 21.1.2/3/5/6 sont la première vérification RÉELLE de D1, corrigé le 11/08 mais validé par l'analyse seule. Si tout passe, le lot D se ferme et le plan perd un à deux jours — c'est le lien L11. COLLISION SUR DOCS, à retenir. La session manager-service avait des modifications stagées et non commitées sur STATUS.md, kanban.html et v1-plan.md, alors que son prompt lui demandait de ne pas toucher à DOCS/. Rien n'est perdu : ses changements et les miens coexistent, et ils sont tous dans ce commit — deux cartes ferées de son côté (tests du RAG, dettes serveur audit & plafond users) plus ses done-items. Ce que ça a produit : mes compteurs de kanban étaient calculés par arithmétique à partir d'un état déjà périmé, donc faux de trois cartes. Ils sont désormais recalés sur la MESURE — on compte les <article> et on écrit le résultat, on ne raisonne plus par delta. Planifié 19, six colonnes cohérentes, 44 done-items, bandeau et libellé alignés. Leçon pour la suite : un compteur dérivé d'un delta est faux dès qu'une autre main touche le fichier. Mesurer, toujours. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
468 lines
17 KiB
HTML
468 lines
17 KiB
HTML
<title>D0 — Visite hors ligne, checklist terrain</title>
|
|
|
|
<style>
|
|
:root {
|
|
--paper: #FBFAF7;
|
|
--card: #FFFFFF;
|
|
--ink: #1B1D1A;
|
|
--ink-2: #4E534C;
|
|
--ink-3: #868C83;
|
|
--line: #DFDDD4;
|
|
--line-soft: #ECEAE2;
|
|
--accent: #3F5D3B;
|
|
--accent-soft:#E4EBE1;
|
|
--alert: #A4442A;
|
|
--alert-soft:#F6E6E0;
|
|
--ok: #2F6B4F;
|
|
|
|
--display: ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
|
|
--body: ui-sans-serif, "Segoe UI", system-ui, -apple-system, sans-serif;
|
|
--mono: ui-monospace, "Cascadia Mono", Consolas, "SF Mono", monospace;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-theme="light"]) {
|
|
--paper: #141613;
|
|
--card: #1C1F1B;
|
|
--ink: #E9EBE5;
|
|
--ink-2: #AFB5AA;
|
|
--ink-3: #7B8177;
|
|
--line: #2E322C;
|
|
--line-soft: #262A24;
|
|
--accent: #9CBE93;
|
|
--accent-soft:#232C21;
|
|
--alert: #E39075;
|
|
--alert-soft:#2C1F19;
|
|
--ok: #7FC0A0;
|
|
}
|
|
}
|
|
|
|
:root[data-theme="dark"] {
|
|
--paper: #141613;
|
|
--card: #1C1F1B;
|
|
--ink: #E9EBE5;
|
|
--ink-2: #AFB5AA;
|
|
--ink-3: #7B8177;
|
|
--line: #2E322C;
|
|
--line-soft: #262A24;
|
|
--accent: #9CBE93;
|
|
--accent-soft:#232C21;
|
|
--alert: #E39075;
|
|
--alert-soft:#2C1F19;
|
|
--ok: #7FC0A0;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
body {
|
|
background: var(--paper);
|
|
color: var(--ink);
|
|
font-family: var(--body);
|
|
font-size: 16px;
|
|
line-height: 1.6;
|
|
margin: 0;
|
|
padding: 2.5rem 1rem 5rem;
|
|
}
|
|
|
|
.page { max-width: 56rem; margin: 0 auto; display: flex; flex-direction: column; gap: 3rem; }
|
|
|
|
header { display: flex; flex-direction: column; gap: 0.7rem; }
|
|
|
|
.eyebrow {
|
|
font-family: var(--mono); font-size: 0.7rem;
|
|
letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3);
|
|
display: flex; flex-wrap: wrap; gap: 0.5rem 1.2rem;
|
|
}
|
|
|
|
h1 {
|
|
font-family: var(--display);
|
|
font-size: clamp(1.9rem, 4.6vw, 2.8rem);
|
|
line-height: 1.1; font-weight: 600; margin: 0;
|
|
letter-spacing: -0.012em; text-wrap: balance;
|
|
}
|
|
|
|
.standfirst { font-size: 1.08rem; color: var(--ink-2); margin: 0; max-width: 60ch; text-wrap: pretty; }
|
|
|
|
h2 {
|
|
font-family: var(--display);
|
|
font-size: 1.45rem; font-weight: 600; margin: 0;
|
|
line-height: 1.2; letter-spacing: -0.008em;
|
|
}
|
|
|
|
section { display: flex; flex-direction: column; gap: 1.2rem; }
|
|
|
|
p { margin: 0; max-width: 68ch; text-wrap: pretty; }
|
|
.lede { color: var(--ink-2); }
|
|
|
|
code {
|
|
font-family: var(--mono); font-size: 0.86em;
|
|
background: var(--line-soft); padding: 0.1em 0.35em; border-radius: 3px;
|
|
}
|
|
|
|
pre {
|
|
font-family: var(--mono); font-size: 0.8rem; line-height: 1.6;
|
|
background: var(--card); border: 1px solid var(--line);
|
|
border-radius: 6px; padding: 1rem; margin: 0;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
/* ── Cas de test ────────────────────────────────── */
|
|
|
|
.group { display: flex; flex-direction: column; gap: 0.65rem; }
|
|
|
|
.group-head {
|
|
display: flex; align-items: baseline; gap: 0.7rem;
|
|
border-bottom: 2px solid var(--line); padding-bottom: 0.5rem;
|
|
}
|
|
.group-head h3 { font-size: 1.05rem; margin: 0; font-weight: 650; }
|
|
.group-head .verdict {
|
|
font-family: var(--mono); font-size: 0.7rem;
|
|
letter-spacing: 0.06em; text-transform: uppercase;
|
|
color: var(--ink-3); margin-left: auto; text-align: right;
|
|
}
|
|
|
|
.case {
|
|
display: grid;
|
|
grid-template-columns: 3.6rem 1fr 1.6rem;
|
|
gap: 0.9rem;
|
|
align-items: start;
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
border-radius: 6px;
|
|
padding: 0.85rem 0.95rem;
|
|
}
|
|
|
|
.case .id {
|
|
font-family: var(--mono); font-size: 0.78rem; font-weight: 700;
|
|
color: var(--accent); padding-top: 0.15rem;
|
|
}
|
|
.case .what { display: flex; flex-direction: column; gap: 0.3rem; }
|
|
.case .act { font-weight: 600; font-size: 0.97rem; }
|
|
.case .exp { font-size: 0.9rem; color: var(--ink-2); }
|
|
.case .exp b { color: var(--ink); font-weight: 650; }
|
|
|
|
.box {
|
|
width: 1.5rem; height: 1.5rem;
|
|
border: 2px solid var(--line);
|
|
border-radius: 4px;
|
|
margin-top: 0.1rem;
|
|
}
|
|
|
|
.tag {
|
|
display: inline-block;
|
|
font-family: var(--mono); font-size: 0.68rem;
|
|
letter-spacing: 0.04em;
|
|
padding: 0.12rem 0.4rem; border-radius: 3px;
|
|
background: var(--accent-soft); color: var(--accent);
|
|
font-weight: 700;
|
|
}
|
|
.tag.alert { background: var(--alert-soft); color: var(--alert); }
|
|
|
|
/* ── Verdicts ───────────────────────────────────── */
|
|
|
|
.verdicts { display: flex; flex-direction: column; gap: 0; border-top: 1px solid var(--line); }
|
|
.verdict-row {
|
|
display: grid; grid-template-columns: 5rem 1fr;
|
|
gap: 1.2rem; padding: 0.95rem 0;
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
.verdict-row .bug {
|
|
font-family: var(--mono); font-size: 0.8rem; font-weight: 700; color: var(--alert);
|
|
}
|
|
.verdict-row p { font-size: 0.93rem; color: var(--ink-2); }
|
|
.verdict-row b { color: var(--ink); }
|
|
|
|
.callout {
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
border-left: 3px solid var(--accent);
|
|
border-radius: 0 6px 6px 0;
|
|
padding: 1rem 1.1rem;
|
|
display: flex; flex-direction: column; gap: 0.5rem;
|
|
}
|
|
.callout h3 { margin: 0; font-size: 0.95rem; }
|
|
.callout p { font-size: 0.92rem; color: var(--ink-2); }
|
|
|
|
footer {
|
|
font-family: var(--mono); font-size: 0.72rem; color: var(--ink-3);
|
|
border-top: 1px solid var(--line); padding-top: 1rem;
|
|
}
|
|
|
|
@media print {
|
|
body { background: #fff; padding: 1rem; font-size: 11pt; }
|
|
.case, pre, .callout { break-inside: avoid; }
|
|
}
|
|
|
|
@media (max-width: 34rem) {
|
|
.case { grid-template-columns: 3rem 1fr 1.4rem; gap: 0.6rem; }
|
|
.verdict-row { grid-template-columns: 1fr; gap: 0.3rem; }
|
|
}
|
|
</style>
|
|
|
|
<div class="page">
|
|
|
|
<header>
|
|
<div class="eyebrow">
|
|
<span>D0 · test-plan §21</span>
|
|
<span>mymuseum-visitapp</span>
|
|
<span>15 cas</span>
|
|
<span>12 août 2026</span>
|
|
</div>
|
|
<h1>Visite hors ligne — ce qu'on va vraiment mesurer</h1>
|
|
<p class="standfirst">
|
|
Cette passe ne cherche pas à valider l'app : elle cherche à savoir
|
|
<strong>lesquels des quatre bugs offline restants existent encore</strong>. Chaque groupe de cas
|
|
ci-dessous pointe vers un bug précis. Un groupe qui passe élimine son bug ; un groupe qui échoue
|
|
lui donne sa priorité. C'est le seul moyen de trier D2 à D5 autrement qu'au jugé.
|
|
</p>
|
|
</header>
|
|
|
|
<section>
|
|
<h2>Avant de commencer</h2>
|
|
<p class="lede">
|
|
Les trois APK sont construits depuis le 12 août — c'est ce que K5 a débloqué. Prendre le flavor
|
|
du lieu testé, pas <code>dev</code>, pour être sur les vraies données.
|
|
</p>
|
|
<pre>cd mymuseum-visitapp
|
|
adb install -r build/app/outputs/flutter-apk/app-mdlf-debug.apk
|
|
# ou app-fortsaintheribert-debug.apk
|
|
|
|
# 21.1.4 demande d'inspecter les fichiers téléchargés :
|
|
adb shell run-as be.unov.mymuseum.mdlf ls -l files/</pre>
|
|
<div class="callout">
|
|
<h3>Ce qui a changé depuis la dernière fois qu'on a regardé</h3>
|
|
<p>
|
|
<strong>D1 est corrigé.</strong> Le <code>switch</code> de collecte des ressources était
|
|
commenté des deux côtés — 157 lignes mortes dans <code>Export</code>, 126 dans
|
|
<code>Import</code>. Une visite téléchargée n'embarquait que l'image de la configuration,
|
|
celle du loader et l'image de chaque section : ni contenus d'articles, ni audios, ni icônes de
|
|
carte, ni images de quiz. C'est réparé, mais <strong>vérifié par l'analyse seulement</strong>.
|
|
Les cas 21.1.2, 21.1.3, 21.1.5 et 21.1.6 sont donc la <em>première</em> vérification réelle
|
|
de ce correctif — s'ils échouent, ce n'est pas un bug de plus, c'est D1 qui n'a pas tenu.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>21.1 — État réel du téléchargement</h2>
|
|
|
|
<div class="group">
|
|
<div class="group-head">
|
|
<h3>Ce que la visite embarque</h3>
|
|
<span class="verdict">valide D1 · révèle D3</span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.1.1</span>
|
|
<div class="what">
|
|
<span class="act">Télécharger une visite, puis passer en mode avion</span>
|
|
<span class="exp">La visite s'ouvre sans erreur.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.1.2</span>
|
|
<div class="what">
|
|
<span class="act">Ouvrir un Article contenant des images de contenu</span>
|
|
<span class="exp">Les images s'affichent. <b>Premier test réel de D1.</b></span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.1.3</span>
|
|
<div class="what">
|
|
<span class="act">Lancer un audio dans cet Article</span>
|
|
<span class="exp">L'audio se lit. Un échec ici peut venir de D1 <em>ou</em> de D3 — l'extension du fichier le départage, voir 21.1.4.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.1.4</span>
|
|
<div class="what">
|
|
<span class="act">Inspecter le dossier local de la configuration</span>
|
|
<span class="exp">Un fichier par ressource, <b>avec une vraie extension</b>. Un <code>.unknown</code> sur un audio, c'est <span class="tag alert">D3</span> — <code>audio/mp3</code> n'est pas un MIME standard, c'est <code>audio/mpeg</code>.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.1.5</span>
|
|
<div class="what">
|
|
<span class="act">Ouvrir un Quiz avec images de questions et de réponses</span>
|
|
<span class="exp">Les images s'affichent. <b>Test réel de D1.</b></span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.1.6</span>
|
|
<div class="what">
|
|
<span class="act">Ouvrir une SectionMap téléchargée</span>
|
|
<span class="exp">Fond de carte, icônes et points présents. Les icônes d'annotation sont précisément ce que D1 ne collectait pas.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>21.2 — Fraîcheur du contenu</h2>
|
|
|
|
<div class="group">
|
|
<div class="group-head">
|
|
<h3>Ce qui se met à jour, et ce qui se nettoie</h3>
|
|
<span class="verdict">décide D2 et D4</span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.2.1</span>
|
|
<div class="what">
|
|
<span class="act">Remplacer une image dans manager-app (même ressource), re-télécharger</span>
|
|
<span class="exp">L'image mise à jour remplace l'ancienne. Sinon <span class="tag alert">D2</span> — le filtre incrémental teste la <em>présence</em> du fichier, pas sa <b>version</b> : une ressource déjà là n'est jamais re-téléchargée.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.2.2</span>
|
|
<div class="what">
|
|
<span class="act">Relancer un téléchargement sans aucune modification</span>
|
|
<span class="exp">Rien n'est re-téléchargé, message « à jour ». C'est le contrôle inverse de 21.2.1 : si D2 est corrigé trop grossièrement, tout se re-télécharge à chaque fois.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.2.3</span>
|
|
<div class="what">
|
|
<span class="act">Supprimer une ressource dans manager-app, re-télécharger</span>
|
|
<span class="exp">Le fichier local est purgé. Sinon <span class="tag alert">D4</span> — le <code>deleteSync()</code> est commenté. ⚠️ D1 a rebranché <code>usedImageOrAudioIds</code>, qui pilote cette purge : elle <b>peut être réparée sans qu'on l'ait touchée</b>.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>21.3 — Types non disponibles hors ligne</h2>
|
|
|
|
<div class="group">
|
|
<div class="group-head">
|
|
<h3>Ce qui doit dire non proprement</h3>
|
|
<span class="verdict">aucun bug connu — cas de contrôle</span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.3.1</span>
|
|
<div class="what">
|
|
<span class="act">En mode avion, ouvrir une section Weather</span>
|
|
<span class="exp">Message explicite « nécessite une connexion ». <b>Ni écran blanc, ni spinner infini.</b></span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.3.2</span>
|
|
<div class="what">
|
|
<span class="act">Idem sur une section Web</span>
|
|
<span class="exp">Message explicite.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.3.3</span>
|
|
<div class="what">
|
|
<span class="act">Idem sur une section Agenda</span>
|
|
<span class="exp">Message explicite.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.3.4</span>
|
|
<div class="what">
|
|
<span class="act">Ouvrir une SectionVideo pointant sur YouTube, en mode avion</span>
|
|
<span class="exp">Message explicite, <b>distinct d'une vidéo téléversée</b> qui, elle, doit se lire. C'est le cas qui sépare « pas de réseau » de « pas téléchargé ».</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>21.4 — Robustesse</h2>
|
|
|
|
<div class="group">
|
|
<div class="group-head">
|
|
<h3>Ce qui se passe quand ça rate</h3>
|
|
<span class="verdict">décide D5</span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.4.1</span>
|
|
<div class="what">
|
|
<span class="act">Couper le réseau pendant un téléchargement</span>
|
|
<span class="exp">Le nombre d'échecs est remonté et la visite <b>n'est pas annoncée « téléchargée »</b>. Sinon <span class="tag alert">D5</span> — les échecs partent dans un <code>print</code>. C'est le pire des quatre : le visiteur croit avoir sa visite.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
|
|
<div class="case">
|
|
<span class="id">21.4.2</span>
|
|
<div class="what">
|
|
<span class="act">Relancer après un téléchargement partiel</span>
|
|
<span class="exp">Seules les ressources manquantes sont récupérées.</span>
|
|
</div>
|
|
<span class="box"></span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Comment lire le résultat</h2>
|
|
<p class="lede">
|
|
Le but n'est pas une colonne de coches vertes. C'est de rentrer avec quatre verdicts.
|
|
</p>
|
|
|
|
<div class="verdicts">
|
|
<div class="verdict-row">
|
|
<span class="bug">D2</span>
|
|
<p><b>Fraîcheur.</b> Décidé par 21.2.1 et 21.2.2. Si une image remplacée ne remonte pas, le filtre doit porter sur la version et non la présence. <b>Conséquence produit :</b> une correction de contenu ne parvient jamais aux visites déjà téléchargées.</p>
|
|
</div>
|
|
<div class="verdict-row">
|
|
<span class="bug">D3</span>
|
|
<p><b>Extensions.</b> Décidé par 21.1.4, confirmé par 21.1.3. Un <code>.unknown</code> sur un audio suffit à trancher — inutile d'attendre que la lecture échoue.</p>
|
|
</div>
|
|
<div class="verdict-row">
|
|
<span class="bug">D4</span>
|
|
<p><b>Purge.</b> Décidé par 21.2.3. <b>Peut déjà être réparé</b> par effet de bord de D1 : à vérifier avant de planifier quoi que ce soit.</p>
|
|
</div>
|
|
<div class="verdict-row">
|
|
<span class="bug">D5</span>
|
|
<p><b>Échecs silencieux.</b> Décidé par 21.4.1. Le plus grave des quatre parce qu'il est invisible : une visite incomplète s'annonce complète, et l'erreur ne se découvre que dans le fort, sans réseau.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="callout">
|
|
<h3>Si tout passe</h3>
|
|
<p>
|
|
Ce n'est pas un résultat décevant, c'est un résultat qui <strong>ferme le lot D</strong> :
|
|
D2 à D5 tombent, et le plan perd un à deux jours de travail non nécessaire. Le lien
|
|
<strong>L11</strong> dit exactement cela — le §21 conditionne l'<em>urgence</em> des quatre
|
|
bugs, pas leur existence. Noter aussi la plainte d'origine, « l'appli marche mal dans le
|
|
fort » : si rien ne se reproduit ici, c'est qu'elle vient d'ailleurs, et ça vaut d'être su.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<footer>
|
|
DOCS/claude design/checklist-d0-offline.html · dérivée de test-plan.md §21 · reporter les
|
|
résultats dans le §21 et dans STATUS.md
|
|
</footer>
|
|
|
|
</div>
|