erplibre/script/todo/migration_status_tui.py

392 lines
14 KiB
Python
Raw Normal View History

[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
#!/usr/bin/env python3
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
"""L'état d'une migration, en plein écran.
Le rapport texte dit tout, mais il dit tout D'UN COUP : sur une migration
de six paliers il fait plusieurs centaines de lignes, et ce qu'on cherche —
l'étape où ça a cassé, ce que le test de fumée a conclu — se trouve
quelque part au milieu. Un écran qui se parcourt règle exactement cela.
Les données viennent de `migration_status`, comme le rapport texte. Deux
assemblages séparés dériveraient l'un de l'autre sans que rien ne le dise,
et l'on finirait par lire deux états contradictoires de la même migration.
"""
import os
import sys
sys.path.append(
os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))
)
from script.todo import migration_status as status # noqa: E402
try:
from script.todo.todo_i18n import t
except Exception: # pragma: no cover - repli si i18n indisponible
def t(key: str) -> str:
return key
CSS = """
Screen { layout: vertical; }
#head { height: 4; padding: 0 1; background: $panel; color: $text; }
#body { height: 1fr; }
#left { width: 46; border-right: solid $accent; }
#pane { width: 1fr; padding: 0 1; }
"""
def head_text(dct):
"""Ce qui identifie la migration. Toujours visible, jamais à chercher."""
info = status.overview(dct)
lst_test = status.tests_summary(dct)
casse = [x for x in lst_test if x.get("status")]
return (
f"{info['database']} · {info['file']}\n"
f"{t('current step')} : {info['step']}\n"
f"{t('finished')} : {info['updated']}"
f" · {t('duration')} {info['elapsed']}"
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
f" · {len(status.failures(dct))} {t('failed')}"
f" · {len(casse)}/{len(lst_test)} {t('Test results')}"
)
def rows(dct):
"""Les lignes du panneau de gauche : tests d'abord, puis les étapes.
Les tests en tête parce que c'est la question qu'on se pose en ouvrant
cet écran ; les étapes ensuite parce que c'est là qu'on cherche le
détail une fois qu'on sait QUOI chercher.
"""
lst = []
for item in status.tests_summary(dct):
icone, _phrase = status.verdict(item.get("status"))
[FIX] migration state: a test result belongs to a step, not to the run You were right that it read as global — and the cause ran deeper than the display. Twenty step headers go through add_comment_progression against seven through print_step, and the whole version-bump loop uses only the first. The current step was set in the other one, so every tool verdict of every bump carried a stale step. The header itself now becomes the step, at the one place the journal is already cut on. And the summary groups by (step, tool) instead of by tool alone: a migration runs the smoke test at EVERY bump, and one line said « smoke_public_url ✅ » while bump 14 had passed and bump 17 had fallen. Within a step the last verdict still wins, with its run count — that is a repair, not two bumps. The order is the journal's, because sorting step names puts « 4.10 » before « 4.2 ». --- FR --- [FIX] état de migration : un résultat de test appartient à une étape Vous aviez raison, cela se lisait comme global — et la cause était plus profonde que l'affichage. Vingt en-têtes d'étape passent par add_comment_progression contre sept par print_step, et toute la boucle des paliers n'utilise que la première. L'étape courante était posée dans l'autre : chaque verdict d'outil portait donc une étape périmée. L'en-tête devient désormais l'étape, au seul endroit où le journal est déjà découpé. Et le résumé groupe par (étape, outil) : une migration lance le test de fumée à CHAQUE palier, et une seule ligne annonçait « smoke_public_url ✅ » quand le palier 14 était passé et le 17 tombé. Dans une étape, le dernier verdict l'emporte toujours — c'est une réparation, pas deux paliers. Assisted-by: Claude Opus 5
2026-08-19 06:00:21 -04:00
# Le NUMÉRO de l'étape en tête : une migration lance le même outil
# à chaque palier, et sans lui la liste montrait six lignes
# identiques sans dire laquelle appartenait à quel palier.
numero = (item.get("step") or "").split(" - ")[0]
tete = f"{numero} " if numero else ""
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
lst.append(
{
"kind": "test",
[FIX] migration state: a test result belongs to a step, not to the run You were right that it read as global — and the cause ran deeper than the display. Twenty step headers go through add_comment_progression against seven through print_step, and the whole version-bump loop uses only the first. The current step was set in the other one, so every tool verdict of every bump carried a stale step. The header itself now becomes the step, at the one place the journal is already cut on. And the summary groups by (step, tool) instead of by tool alone: a migration runs the smoke test at EVERY bump, and one line said « smoke_public_url ✅ » while bump 14 had passed and bump 17 had fallen. Within a step the last verdict still wins, with its run count — that is a repair, not two bumps. The order is the journal's, because sorting step names puts « 4.10 » before « 4.2 ». --- FR --- [FIX] état de migration : un résultat de test appartient à une étape Vous aviez raison, cela se lisait comme global — et la cause était plus profonde que l'affichage. Vingt en-têtes d'étape passent par add_comment_progression contre sept par print_step, et toute la boucle des paliers n'utilise que la première. L'étape courante était posée dans l'autre : chaque verdict d'outil portait donc une étape périmée. L'en-tête devient désormais l'étape, au seul endroit où le journal est déjà découpé. Et le résumé groupe par (étape, outil) : une migration lance le test de fumée à CHAQUE palier, et une seule ligne annonçait « smoke_public_url ✅ » quand le palier 14 était passé et le 17 tombé. Dans une étape, le dernier verdict l'emporte toujours — c'est une réparation, pas deux paliers. Assisted-by: Claude Opus 5
2026-08-19 06:00:21 -04:00
"label": f"{icone} {tete}{item['name']}",
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
"detail": str(item.get("runs", 1)),
"data": item,
}
)
for section in status.journal_by_step(dct):
[ADD] migration state: read the server log so nobody has to A step log reaches thirteen megabytes — measured on a real migration. Nobody reads that, and the question one asks in front of it is two words long: where did it go wrong? The screen now answers it. Each step carries its count of ERROR and CRITICAL, and the pane lists the DISTINCT messages with how many times each occurred. Forty-eight « Model X has no table » is one problem seen forty-eight times, not forty-eight problems, and the raw list buries everything else. WARNING is left out of the count: a migration produces thousands, and a total that includes them means nothing. Each message keeps its logger and its DATABASE, which is what tells the six bumps apart — they shared one file before a step opened its own. Measured: thirteen megabytes scanned in 0.09 s, seventy occurrences reduced to fifteen messages, and cached on (size, mtime) because the screen repaints on every keystroke. --- FR --- [ADD] état de migration : lire le journal pour que personne n'ait à le faire Un journal d'étape atteint treize mégaoctets — mesuré. Personne ne le lit, et la question qu'on se pose devant lui tient en deux mots : où est-ce que ça a mal tourné ? L'écran y répond désormais. Chaque étape porte son compte d'ERROR et de CRITICAL, et le panneau liste les messages DISTINCTS avec leur nombre. Quarante-huit fois « Model X has no table » est un problème vu quarante-huit fois, pas quarante-huit problèmes. Les WARNING sont hors du compte : une migration en produit des milliers. Chaque message garde son logger et sa BASE, ce qui distingue les six paliers — ils partageaient un fichier avant qu'une étape n'ouvre le sien. Mesuré : treize mégaoctets analysés en 0,09 s, soixante-dix occurrences ramenées à quinze messages. Assisted-by: Claude Opus 5
2026-08-19 07:37:40 -04:00
graves = status.severe_count(
status.step_log_scan(dct, section["step"])
)
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
lst.append(
{
"kind": "step",
"label": section["step"],
"detail": str(len(section["lst_cmd"])),
[ADD] migration state: read the server log so nobody has to A step log reaches thirteen megabytes — measured on a real migration. Nobody reads that, and the question one asks in front of it is two words long: where did it go wrong? The screen now answers it. Each step carries its count of ERROR and CRITICAL, and the pane lists the DISTINCT messages with how many times each occurred. Forty-eight « Model X has no table » is one problem seen forty-eight times, not forty-eight problems, and the raw list buries everything else. WARNING is left out of the count: a migration produces thousands, and a total that includes them means nothing. Each message keeps its logger and its DATABASE, which is what tells the six bumps apart — they shared one file before a step opened its own. Measured: thirteen megabytes scanned in 0.09 s, seventy occurrences reduced to fifteen messages, and cached on (size, mtime) because the screen repaints on every keystroke. --- FR --- [ADD] état de migration : lire le journal pour que personne n'ait à le faire Un journal d'étape atteint treize mégaoctets — mesuré. Personne ne le lit, et la question qu'on se pose devant lui tient en deux mots : où est-ce que ça a mal tourné ? L'écran y répond désormais. Chaque étape porte son compte d'ERROR et de CRITICAL, et le panneau liste les messages DISTINCTS avec leur nombre. Quarante-huit fois « Model X has no table » est un problème vu quarante-huit fois, pas quarante-huit problèmes. Les WARNING sont hors du compte : une migration en produit des milliers. Chaque message garde son logger et sa BASE, ce qui distingue les six paliers — ils partageaient un fichier avant qu'une étape n'ouvre le sien. Mesuré : treize mégaoctets analysés en 0,09 s, soixante-dix occurrences ramenées à quinze messages. Assisted-by: Claude Opus 5
2026-08-19 07:37:40 -04:00
"severe": str(graves) if graves else "",
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
"data": section,
}
)
return lst
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
def pane_text(dct, row, colour=False, show_log=True):
[ADD] migration state: colour the commands, and know when not to A list of commands in flat text blends into its own headings the moment it outgrows the screen — which is exactly when it gets read. Commands are now cyan, steps bold blue, failed ones red, and each verdict wears the colour of its meaning: 1 is amber, because « there is something to look at » is not a crash and painting it red worries for nothing. Colour is refused three times over, and each refusal has cost someone: a log file full of escape codes, a grep that finds nothing, a terminal that prints them as text. A pipe is not a screen, and NO_COLOR is honoured. The full screen goes through Text.from_ansi, which does two things: it renders the colours, and it takes the rest literally. Textual parses Rich markup, so a command containing « [1] » was being eaten as a tag and vanished from the pane without a word. That was true before the colour. --- FR --- [ADD] état de migration : colorer les commandes, et savoir s'en abstenir Une liste de commandes en texte plat se confond avec ses titres dès qu'elle dépasse l'écran — et c'est justement quand elle le dépasse qu'on la lit. Les commandes sont en cyan, les étapes en bleu gras, celles qui ont échoué en rouge, et chaque verdict porte la couleur de son sens : 1 est ambre, car « il y a quelque chose à regarder » n'est pas une panne. La couleur se refuse trois fois, et chaque refus a coûté à quelqu'un : un fichier de journal truffé de codes d'échappement, un grep qui ne trouve plus rien, un terminal qui les affiche en clair. Le plein écran passe par Text.from_ansi, qui rend les couleurs ET prend le reste au pied de la lettre : une commande contenant « [1] » était avalée comme du balisage Rich et disparaissait sans un mot. Assisted-by: Claude Opus 5
2026-08-19 05:36:49 -04:00
"""Le détail de la ligne choisie.
Le coloriage passe par de l'ANSI, que Rich sait décoder — et qui rend
au passage le texte LITTÉRAL : une commande contenant « [1] » était
jusqu'ici prise pour du balisage Rich et avalée sans un mot.
"""
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
if row is None:
return t("Nothing to show yet.")
if row["kind"] == "test":
item = row["data"]
icone, phrase = status.verdict(item.get("status"))
[ADD] migration state: colour the commands, and know when not to A list of commands in flat text blends into its own headings the moment it outgrows the screen — which is exactly when it gets read. Commands are now cyan, steps bold blue, failed ones red, and each verdict wears the colour of its meaning: 1 is amber, because « there is something to look at » is not a crash and painting it red worries for nothing. Colour is refused three times over, and each refusal has cost someone: a log file full of escape codes, a grep that finds nothing, a terminal that prints them as text. A pipe is not a screen, and NO_COLOR is honoured. The full screen goes through Text.from_ansi, which does two things: it renders the colours, and it takes the rest literally. Textual parses Rich markup, so a command containing « [1] » was being eaten as a tag and vanished from the pane without a word. That was true before the colour. --- FR --- [ADD] état de migration : colorer les commandes, et savoir s'en abstenir Une liste de commandes en texte plat se confond avec ses titres dès qu'elle dépasse l'écran — et c'est justement quand elle le dépasse qu'on la lit. Les commandes sont en cyan, les étapes en bleu gras, celles qui ont échoué en rouge, et chaque verdict porte la couleur de son sens : 1 est ambre, car « il y a quelque chose à regarder » n'est pas une panne. La couleur se refuse trois fois, et chaque refus a coûté à quelqu'un : un fichier de journal truffé de codes d'échappement, un grep qui ne trouve plus rien, un terminal qui les affiche en clair. Le plein écran passe par Text.from_ansi, qui rend les couleurs ET prend le reste au pied de la lettre : une commande contenant « [1] » était avalée comme du balisage Rich et disparaissait sans un mot. Assisted-by: Claude Opus 5
2026-08-19 05:36:49 -04:00
teinte = status.VERDICT_COLOUR.get(item.get("status"), "dim")
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
lignes = [
[ADD] migration state: colour the commands, and know when not to A list of commands in flat text blends into its own headings the moment it outgrows the screen — which is exactly when it gets read. Commands are now cyan, steps bold blue, failed ones red, and each verdict wears the colour of its meaning: 1 is amber, because « there is something to look at » is not a crash and painting it red worries for nothing. Colour is refused three times over, and each refusal has cost someone: a log file full of escape codes, a grep that finds nothing, a terminal that prints them as text. A pipe is not a screen, and NO_COLOR is honoured. The full screen goes through Text.from_ansi, which does two things: it renders the colours, and it takes the rest literally. Textual parses Rich markup, so a command containing « [1] » was being eaten as a tag and vanished from the pane without a word. That was true before the colour. --- FR --- [ADD] état de migration : colorer les commandes, et savoir s'en abstenir Une liste de commandes en texte plat se confond avec ses titres dès qu'elle dépasse l'écran — et c'est justement quand elle le dépasse qu'on la lit. Les commandes sont en cyan, les étapes en bleu gras, celles qui ont échoué en rouge, et chaque verdict porte la couleur de son sens : 1 est ambre, car « il y a quelque chose à regarder » n'est pas une panne. La couleur se refuse trois fois, et chaque refus a coûté à quelqu'un : un fichier de journal truffé de codes d'échappement, un grep qui ne trouve plus rien, un terminal qui les affiche en clair. Le plein écran passe par Text.from_ansi, qui rend les couleurs ET prend le reste au pied de la lettre : une commande contenant « [1] » était avalée comme du balisage Rich et disparaissait sans un mot. Assisted-by: Claude Opus 5
2026-08-19 05:36:49 -04:00
f"{icone} {status.paint(item['name'], teinte, colour)}",
[FIX] migration state: a test result belongs to a step, not to the run You were right that it read as global — and the cause ran deeper than the display. Twenty step headers go through add_comment_progression against seven through print_step, and the whole version-bump loop uses only the first. The current step was set in the other one, so every tool verdict of every bump carried a stale step. The header itself now becomes the step, at the one place the journal is already cut on. And the summary groups by (step, tool) instead of by tool alone: a migration runs the smoke test at EVERY bump, and one line said « smoke_public_url ✅ » while bump 14 had passed and bump 17 had fallen. Within a step the last verdict still wins, with its run count — that is a repair, not two bumps. The order is the journal's, because sorting step names puts « 4.10 » before « 4.2 ». --- FR --- [FIX] état de migration : un résultat de test appartient à une étape Vous aviez raison, cela se lisait comme global — et la cause était plus profonde que l'affichage. Vingt en-têtes d'étape passent par add_comment_progression contre sept par print_step, et toute la boucle des paliers n'utilise que la première. L'étape courante était posée dans l'autre : chaque verdict d'outil portait donc une étape périmée. L'en-tête devient désormais l'étape, au seul endroit où le journal est déjà découpé. Et le résumé groupe par (étape, outil) : une migration lance le test de fumée à CHAQUE palier, et une seule ligne annonçait « smoke_public_url ✅ » quand le palier 14 était passé et le 17 tombé. Dans une étape, le dernier verdict l'emporte toujours — c'est une réparation, pas deux paliers. Assisted-by: Claude Opus 5
2026-08-19 06:00:21 -04:00
f" {status.paint(item.get('step') or '?', 'step', colour)}",
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
f" {phrase} ({t('exit code')} {item.get('status')})",
f" {t('runs')} : {item.get('runs')}",
f" {item.get('at') or ''}",
]
return "\n".join(lignes)
section = row["data"]
[ADD] migration state: colour the commands, and know when not to A list of commands in flat text blends into its own headings the moment it outgrows the screen — which is exactly when it gets read. Commands are now cyan, steps bold blue, failed ones red, and each verdict wears the colour of its meaning: 1 is amber, because « there is something to look at » is not a crash and painting it red worries for nothing. Colour is refused three times over, and each refusal has cost someone: a log file full of escape codes, a grep that finds nothing, a terminal that prints them as text. A pipe is not a screen, and NO_COLOR is honoured. The full screen goes through Text.from_ansi, which does two things: it renders the colours, and it takes the rest literally. Textual parses Rich markup, so a command containing « [1] » was being eaten as a tag and vanished from the pane without a word. That was true before the colour. --- FR --- [ADD] état de migration : colorer les commandes, et savoir s'en abstenir Une liste de commandes en texte plat se confond avec ses titres dès qu'elle dépasse l'écran — et c'est justement quand elle le dépasse qu'on la lit. Les commandes sont en cyan, les étapes en bleu gras, celles qui ont échoué en rouge, et chaque verdict porte la couleur de son sens : 1 est ambre, car « il y a quelque chose à regarder » n'est pas une panne. La couleur se refuse trois fois, et chaque refus a coûté à quelqu'un : un fichier de journal truffé de codes d'échappement, un grep qui ne trouve plus rien, un terminal qui les affiche en clair. Le plein écran passe par Text.from_ansi, qui rend les couleurs ET prend le reste au pied de la lettre : une commande contenant « [1] » était avalée comme du balisage Rich et disparaissait sans un mot. Assisted-by: Claude Opus 5
2026-08-19 05:36:49 -04:00
lignes = [status.paint(section["step"], "step", colour), ""]
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
lst_failure = [
item
for item in status.failures(dct)
if (item.get("step") or "") == section["step"]
]
if lst_failure:
lignes.append(f"❌ {t('Commands that failed')} :")
for item in lst_failure:
[ADD] migration state: colour the commands, and know when not to A list of commands in flat text blends into its own headings the moment it outgrows the screen — which is exactly when it gets read. Commands are now cyan, steps bold blue, failed ones red, and each verdict wears the colour of its meaning: 1 is amber, because « there is something to look at » is not a crash and painting it red worries for nothing. Colour is refused three times over, and each refusal has cost someone: a log file full of escape codes, a grep that finds nothing, a terminal that prints them as text. A pipe is not a screen, and NO_COLOR is honoured. The full screen goes through Text.from_ansi, which does two things: it renders the colours, and it takes the rest literally. Textual parses Rich markup, so a command containing « [1] » was being eaten as a tag and vanished from the pane without a word. That was true before the colour. --- FR --- [ADD] état de migration : colorer les commandes, et savoir s'en abstenir Une liste de commandes en texte plat se confond avec ses titres dès qu'elle dépasse l'écran — et c'est justement quand elle le dépasse qu'on la lit. Les commandes sont en cyan, les étapes en bleu gras, celles qui ont échoué en rouge, et chaque verdict porte la couleur de son sens : 1 est ambre, car « il y a quelque chose à regarder » n'est pas une panne. La couleur se refuse trois fois, et chaque refus a coûté à quelqu'un : un fichier de journal truffé de codes d'échappement, un grep qui ne trouve plus rien, un terminal qui les affiche en clair. Le plein écran passe par Text.from_ansi, qui rend les couleurs ET prend le reste au pied de la lettre : une commande contenant « [1] » était avalée comme du balisage Rich et disparaissait sans un mot. Assisted-by: Claude Opus 5
2026-08-19 05:36:49 -04:00
lignes.append(
f" {status.paint(item.get('name') or '', 'fail', colour)}"
)
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
lignes.append("")
for cmd in section["lst_cmd"]:
[ADD] migration state: colour the commands, and know when not to A list of commands in flat text blends into its own headings the moment it outgrows the screen — which is exactly when it gets read. Commands are now cyan, steps bold blue, failed ones red, and each verdict wears the colour of its meaning: 1 is amber, because « there is something to look at » is not a crash and painting it red worries for nothing. Colour is refused three times over, and each refusal has cost someone: a log file full of escape codes, a grep that finds nothing, a terminal that prints them as text. A pipe is not a screen, and NO_COLOR is honoured. The full screen goes through Text.from_ansi, which does two things: it renders the colours, and it takes the rest literally. Textual parses Rich markup, so a command containing « [1] » was being eaten as a tag and vanished from the pane without a word. That was true before the colour. --- FR --- [ADD] état de migration : colorer les commandes, et savoir s'en abstenir Une liste de commandes en texte plat se confond avec ses titres dès qu'elle dépasse l'écran — et c'est justement quand elle le dépasse qu'on la lit. Les commandes sont en cyan, les étapes en bleu gras, celles qui ont échoué en rouge, et chaque verdict porte la couleur de son sens : 1 est ambre, car « il y a quelque chose à regarder » n'est pas une panne. La couleur se refuse trois fois, et chaque refus a coûté à quelqu'un : un fichier de journal truffé de codes d'échappement, un grep qui ne trouve plus rien, un terminal qui les affiche en clair. Le plein écran passe par Text.from_ansi, qui rend les couleurs ET prend le reste au pied de la lettre : une commande contenant « [1] » était avalée comme du balisage Rich et disparaissait sans un mot. Assisted-by: Claude Opus 5
2026-08-19 05:36:49 -04:00
lignes.append(f"· {status.paint(cmd, 'cmd', colour)}")
[ADD] migration: keep the logs on disk, one file per step The state screen showed « no tool has run yet » after closing and reopening. It was reading the progression file, and that file is archived and reset when a migration restarts — so everything it knew vanished at the exact moment one wants to understand why the restart was needed. Each step now has its own log file, appended never overwritten, and the failures and tool verdicts go to an append-only JSONL beside them. Both outlive the progression, and the screen reads disk first, memory second, without counting the overlap twice. The command output itself is captured where every line already passes, in the executor's read loop: the terminal still shows it live and nothing about the run changes. What goes through the real terminal cannot be captured — a pipe there makes full screens refuse, that lesson is paid — so those keep at least their command and their exit code. --- FR --- [ADD] migration : garder les journaux sur disque, un fichier par étape L'écran d'état affichait « aucun outil n'a encore tourné » après une fermeture. Il lisait le fichier de progression, or celui-ci est archivé puis remis à zéro quand on recommence : tout ce qu'il savait disparaissait au moment précis où l'on cherche pourquoi il a fallu recommencer. Chaque étape a désormais son fichier, en ajout et jamais en écrasement, et les échecs comme les verdicts d'outils vont dans un JSONL à côté. Les deux survivent à la progression, et l'écran lit le disque d'abord. La sortie des commandes est captée là où chaque ligne passe déjà, dans la boucle de lecture de l'exécuteur : le terminal la montre toujours en direct. Ce qui passe par le vrai terminal n'est pas captable — un tube y ferait renoncer les pleins écrans — et garde au moins son verdict. Assisted-by: Claude Opus 5
2026-08-19 04:32:25 -04:00
# La SORTIE des commandes, relue sur disque. C'est ce qui manquait :
# la liste des commandes dit ce qui a été lancé, jamais ce que cela a
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
# répondu. Mais les deux mélangés dans un même panneau se confondent —
# d'où « l », qui les sépare.
[ADD] migration state: read the server log so nobody has to A step log reaches thirteen megabytes — measured on a real migration. Nobody reads that, and the question one asks in front of it is two words long: where did it go wrong? The screen now answers it. Each step carries its count of ERROR and CRITICAL, and the pane lists the DISTINCT messages with how many times each occurred. Forty-eight « Model X has no table » is one problem seen forty-eight times, not forty-eight problems, and the raw list buries everything else. WARNING is left out of the count: a migration produces thousands, and a total that includes them means nothing. Each message keeps its logger and its DATABASE, which is what tells the six bumps apart — they shared one file before a step opened its own. Measured: thirteen megabytes scanned in 0.09 s, seventy occurrences reduced to fifteen messages, and cached on (size, mtime) because the screen repaints on every keystroke. --- FR --- [ADD] état de migration : lire le journal pour que personne n'ait à le faire Un journal d'étape atteint treize mégaoctets — mesuré. Personne ne le lit, et la question qu'on se pose devant lui tient en deux mots : où est-ce que ça a mal tourné ? L'écran y répond désormais. Chaque étape porte son compte d'ERROR et de CRITICAL, et le panneau liste les messages DISTINCTS avec leur nombre. Quarante-huit fois « Model X has no table » est un problème vu quarante-huit fois, pas quarante-huit problèmes. Les WARNING sont hors du compte : une migration en produit des milliers. Chaque message garde son logger et sa BASE, ce qui distingue les six paliers — ils partageaient un fichier avant qu'une étape n'ouvre le sien. Mesuré : treize mégaoctets analysés en 0,09 s, soixante-dix occurrences ramenées à quinze messages. Assisted-by: Claude Opus 5
2026-08-19 07:37:40 -04:00
# Les erreurs DISTINCTES d'abord, le journal brut ensuite : quarante-
# huit fois le même message est un problème vu quarante-huit fois, et
# la liste brute le noie au milieu de cent mille lignes.
scan = status.step_log_scan(dct, section["step"])
if scan["errors"]:
lignes.append("")
lignes.append(
f"── {t('errors in the log')} :" f" {status.severe_count(scan)} ──"
)
for item in scan["errors"][:15]:
lignes.append(
f" ×{item['times']:<4}"
f" {status.paint(item['message'], 'fail', colour)}"
)
lignes.append(
f" {status.paint(item['logger'], 'dim', colour)}"
f" · {item['database']}"
)
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
tail, total = status.step_log_tail(dct, section["step"])
if not show_log:
if total:
lignes.append("")
lignes.append(
f"── {t('server log')} : {total} {t('lines')}"
f" ({t('press l to show')}) ──"
)
return "\n".join(lignes)
[ADD] migration: keep the logs on disk, one file per step The state screen showed « no tool has run yet » after closing and reopening. It was reading the progression file, and that file is archived and reset when a migration restarts — so everything it knew vanished at the exact moment one wants to understand why the restart was needed. Each step now has its own log file, appended never overwritten, and the failures and tool verdicts go to an append-only JSONL beside them. Both outlive the progression, and the screen reads disk first, memory second, without counting the overlap twice. The command output itself is captured where every line already passes, in the executor's read loop: the terminal still shows it live and nothing about the run changes. What goes through the real terminal cannot be captured — a pipe there makes full screens refuse, that lesson is paid — so those keep at least their command and their exit code. --- FR --- [ADD] migration : garder les journaux sur disque, un fichier par étape L'écran d'état affichait « aucun outil n'a encore tourné » après une fermeture. Il lisait le fichier de progression, or celui-ci est archivé puis remis à zéro quand on recommence : tout ce qu'il savait disparaissait au moment précis où l'on cherche pourquoi il a fallu recommencer. Chaque étape a désormais son fichier, en ajout et jamais en écrasement, et les échecs comme les verdicts d'outils vont dans un JSONL à côté. Les deux survivent à la progression, et l'écran lit le disque d'abord. La sortie des commandes est captée là où chaque ligne passe déjà, dans la boucle de lecture de l'exécuteur : le terminal la montre toujours en direct. Ce qui passe par le vrai terminal n'est pas captable — un tube y ferait renoncer les pleins écrans — et garde au moins son verdict. Assisted-by: Claude Opus 5
2026-08-19 04:32:25 -04:00
if tail:
lignes.append("")
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
cache = (
f" — {t('last')} {len(tail)} {t('of')} {total}"
if total > len(tail)
else ""
)
lignes.append(f"── {t('server log')}{cache} ──")
[ADD] migration: keep the logs on disk, one file per step The state screen showed « no tool has run yet » after closing and reopening. It was reading the progression file, and that file is archived and reset when a migration restarts — so everything it knew vanished at the exact moment one wants to understand why the restart was needed. Each step now has its own log file, appended never overwritten, and the failures and tool verdicts go to an append-only JSONL beside them. Both outlive the progression, and the screen reads disk first, memory second, without counting the overlap twice. The command output itself is captured where every line already passes, in the executor's read loop: the terminal still shows it live and nothing about the run changes. What goes through the real terminal cannot be captured — a pipe there makes full screens refuse, that lesson is paid — so those keep at least their command and their exit code. --- FR --- [ADD] migration : garder les journaux sur disque, un fichier par étape L'écran d'état affichait « aucun outil n'a encore tourné » après une fermeture. Il lisait le fichier de progression, or celui-ci est archivé puis remis à zéro quand on recommence : tout ce qu'il savait disparaissait au moment précis où l'on cherche pourquoi il a fallu recommencer. Chaque étape a désormais son fichier, en ajout et jamais en écrasement, et les échecs comme les verdicts d'outils vont dans un JSONL à côté. Les deux survivent à la progression, et l'écran lit le disque d'abord. La sortie des commandes est captée là où chaque ligne passe déjà, dans la boucle de lecture de l'exécuteur : le terminal la montre toujours en direct. Ce qui passe par le vrai terminal n'est pas captable — un tube y ferait renoncer les pleins écrans — et garde au moins son verdict. Assisted-by: Claude Opus 5
2026-08-19 04:32:25 -04:00
lignes.extend(tail)
elif not section["lst_cmd"]:
lignes.append(t("No tool has run yet."))
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
return "\n".join(lignes)
[ADD] migration: announce the countdown, and cycle three panel states Every prompt now carries « ⏱5s » before the question. One that looks like it waits forever does not invite you to step away, which is the whole point of auto-run. The same clock reports the answer afterwards: it announces the wait, then says what it decided. Enter on the auto-run question already meant no; it now says so, with an explicit default instead of a deduction. And the flag is cleared before asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have answered yes on someone's behalf, and that answer commits every later one. « p » cycles three states rather than toggling one: a plain toggle freed four lines, while what takes the room is the left COLUMN — on a server log every character of width counts. All → no summary → detail only, and back. The state is named in the sub-title, which stays visible in all three: a panel that vanishes without a word reads as a broken screen. --- FR --- [ADD] migration : annoncer le compte à rebours, et trois états de panneaux Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite. Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le drapeau est effacé avant de demander : une variable héritée du shell aurait répondu « oui » à la place de quelqu'un. « p » enchaîne trois états au lieu d'en basculer un : ce qui prend la place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et l'on revient. L'état est nommé dans le sous-titre. Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
PANELS = ("all", "no-summary", "detail")
def panel_label(state):
"""Le nom de l'état courant, tel qu'il s'affiche dans le sous-titre."""
return {
"all": t("summary + list + detail"),
"no-summary": t("list + detail"),
"detail": t("detail only"),
}[PANELS[state % len(PANELS)]]
def panel_visibility(state):
"""(résumé visible, liste visible) pour cet état.
Trois états, du plus complet au plus dépouillé. Un simple bascule ne
libérait que quatre lignes ; ce qui prend la place, c'est la COLONNE de
gauche — et sur un journal de serveur, chaque caractère gagné en
largeur compte.
"""
etat = PANELS[state % len(PANELS)]
return etat == "all", etat != "detail"
def current_row(lst_row, index):
"""La ligne choisie, ou None quand il n'y en a aucune."""
return lst_row[index] if lst_row and 0 <= index < len(lst_row) else None
def reread(path):
"""Relire la progression, ou None si l'on ne sait pas d'où."""
return status.read(path) if path else None
def apply_panels(app):
"""Poser la visibilité des panneaux, et NOMMER l'état choisi.
Le sous-titre reste visible dans les trois : un panneau qui disparaît
sans un mot se lit comme un écran cassé.
"""
resume, liste = panel_visibility(app.panel_state)
app.query_one("#head").display = resume
app.query_one("#left").display = liste
app.sub_title = panel_label(app.panel_state)
def apply_width(app, delta):
"""Déplacer la séparation, bornée des deux côtés.
Une colonne de zéro ne se retrouve plus ; une qui mange tout l'écran ne
laisse rien à lire.
"""
app.left_width = max(
app.LEFT_MIN, min(app.LEFT_MAX, app.left_width + delta)
)
app.query_one("#left").styles.width = app.left_width
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
def build_app(dct, path=None):
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
"""Textual est importé ICI, pas au chargement du module.
Le module reste importable — donc testable — sur une machine sans
Textual, et c'est aussi ce qui permet à l'appelant de retomber sur le
rapport texte plutôt que d'échouer.
"""
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
from rich.text import Text
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
from textual.app import App, ComposeResult
from textual.containers import Horizontal, VerticalScroll
from textual.widgets import DataTable, Footer, Header, Static
class StatusApp(App):
CSS = globals()["CSS"]
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
BINDINGS = [
("q,escape", "quit", t("Quit")),
("r", "refresh", t("Refresh")),
("l", "toggle_log", t("Logs")),
[ADD] migration: announce the countdown, and cycle three panel states Every prompt now carries « ⏱5s » before the question. One that looks like it waits forever does not invite you to step away, which is the whole point of auto-run. The same clock reports the answer afterwards: it announces the wait, then says what it decided. Enter on the auto-run question already meant no; it now says so, with an explicit default instead of a deduction. And the flag is cleared before asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have answered yes on someone's behalf, and that answer commits every later one. « p » cycles three states rather than toggling one: a plain toggle freed four lines, while what takes the room is the left COLUMN — on a server log every character of width counts. All → no summary → detail only, and back. The state is named in the sub-title, which stays visible in all three: a panel that vanishes without a word reads as a broken screen. --- FR --- [ADD] migration : annoncer le compte à rebours, et trois états de panneaux Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite. Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le drapeau est effacé avant de demander : une variable héritée du shell aurait répondu « oui » à la place de quelqu'un. « p » enchaîne trois états au lieu d'en basculer un : ce qui prend la place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et l'on revient. L'état est nommé dans le sous-titre. Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
("p", "cycle_panels", t("Panels")),
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
("plus,equal", "wider", t("Wider")),
("minus,underscore", "narrower", t("Narrower")),
]
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
LEFT_MIN = 16
LEFT_MAX = 110
LEFT_STEP = 6
def __init__(self, dct, path=None):
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
super().__init__()
self.dct = dct
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
self.path = path
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
self.lst_row = rows(dct)
self.index = 0
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
self.show_log = True
self.left_width = 46
[ADD] migration: announce the countdown, and cycle three panel states Every prompt now carries « ⏱5s » before the question. One that looks like it waits forever does not invite you to step away, which is the whole point of auto-run. The same clock reports the answer afterwards: it announces the wait, then says what it decided. Enter on the auto-run question already meant no; it now says so, with an explicit default instead of a deduction. And the flag is cleared before asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have answered yes on someone's behalf, and that answer commits every later one. « p » cycles three states rather than toggling one: a plain toggle freed four lines, while what takes the room is the left COLUMN — on a server log every character of width counts. All → no summary → detail only, and back. The state is named in the sub-title, which stays visible in all three: a panel that vanishes without a word reads as a broken screen. --- FR --- [ADD] migration : annoncer le compte à rebours, et trois états de panneaux Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite. Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le drapeau est effacé avant de demander : une variable héritée du shell aurait répondu « oui » à la place de quelqu'un. « p » enchaîne trois états au lieu d'en basculer un : ce qui prend la place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et l'on revient. L'état est nommé dans le sous-titre. Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
self.panel_state = 0
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
def compose(self) -> ComposeResult:
yield Header()
yield Static("", id="head")
with Horizontal(id="body"):
yield DataTable(id="left", cursor_type="row")
with VerticalScroll(id="pane"):
yield Static("", id="content")
yield Footer()
def on_mount(self):
self.title = t("Migration state")
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
self._fill_table()
[ADD] migration: announce the countdown, and cycle three panel states Every prompt now carries « ⏱5s » before the question. One that looks like it waits forever does not invite you to step away, which is the whole point of auto-run. The same clock reports the answer afterwards: it announces the wait, then says what it decided. Enter on the auto-run question already meant no; it now says so, with an explicit default instead of a deduction. And the flag is cleared before asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have answered yes on someone's behalf, and that answer commits every later one. « p » cycles three states rather than toggling one: a plain toggle freed four lines, while what takes the room is the left COLUMN — on a server log every character of width counts. All → no summary → detail only, and back. The state is named in the sub-title, which stays visible in all three: a panel that vanishes without a word reads as a broken screen. --- FR --- [ADD] migration : annoncer le compte à rebours, et trois états de panneaux Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite. Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le drapeau est effacé avant de demander : une variable héritée du shell aurait répondu « oui » à la place de quelqu'un. « p » enchaîne trois états au lieu d'en basculer un : ce qui prend la place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et l'on revient. L'état est nommé dans le sous-titre. Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
apply_panels(self)
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
self._show()
def _fill_table(self):
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
table = self.query_one("#left", DataTable)
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
table.clear(columns=True)
[ADD] migration state: read the server log so nobody has to A step log reaches thirteen megabytes — measured on a real migration. Nobody reads that, and the question one asks in front of it is two words long: where did it go wrong? The screen now answers it. Each step carries its count of ERROR and CRITICAL, and the pane lists the DISTINCT messages with how many times each occurred. Forty-eight « Model X has no table » is one problem seen forty-eight times, not forty-eight problems, and the raw list buries everything else. WARNING is left out of the count: a migration produces thousands, and a total that includes them means nothing. Each message keeps its logger and its DATABASE, which is what tells the six bumps apart — they shared one file before a step opened its own. Measured: thirteen megabytes scanned in 0.09 s, seventy occurrences reduced to fifteen messages, and cached on (size, mtime) because the screen repaints on every keystroke. --- FR --- [ADD] état de migration : lire le journal pour que personne n'ait à le faire Un journal d'étape atteint treize mégaoctets — mesuré. Personne ne le lit, et la question qu'on se pose devant lui tient en deux mots : où est-ce que ça a mal tourné ? L'écran y répond désormais. Chaque étape porte son compte d'ERROR et de CRITICAL, et le panneau liste les messages DISTINCTS avec leur nombre. Quarante-huit fois « Model X has no table » est un problème vu quarante-huit fois, pas quarante-huit problèmes. Les WARNING sont hors du compte : une migration en produit des milliers. Chaque message garde son logger et sa BASE, ce qui distingue les six paliers — ils partageaient un fichier avant qu'une étape n'ouvre le sien. Mesuré : treize mégaoctets analysés en 0,09 s, soixante-dix occurrences ramenées à quinze messages. Assisted-by: Claude Opus 5
2026-08-19 07:37:40 -04:00
table.add_columns(t("Test results"), "#", "❌")
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
for row in self.lst_row:
[ADD] migration state: read the server log so nobody has to A step log reaches thirteen megabytes — measured on a real migration. Nobody reads that, and the question one asks in front of it is two words long: where did it go wrong? The screen now answers it. Each step carries its count of ERROR and CRITICAL, and the pane lists the DISTINCT messages with how many times each occurred. Forty-eight « Model X has no table » is one problem seen forty-eight times, not forty-eight problems, and the raw list buries everything else. WARNING is left out of the count: a migration produces thousands, and a total that includes them means nothing. Each message keeps its logger and its DATABASE, which is what tells the six bumps apart — they shared one file before a step opened its own. Measured: thirteen megabytes scanned in 0.09 s, seventy occurrences reduced to fifteen messages, and cached on (size, mtime) because the screen repaints on every keystroke. --- FR --- [ADD] état de migration : lire le journal pour que personne n'ait à le faire Un journal d'étape atteint treize mégaoctets — mesuré. Personne ne le lit, et la question qu'on se pose devant lui tient en deux mots : où est-ce que ça a mal tourné ? L'écran y répond désormais. Chaque étape porte son compte d'ERROR et de CRITICAL, et le panneau liste les messages DISTINCTS avec leur nombre. Quarante-huit fois « Model X has no table » est un problème vu quarante-huit fois, pas quarante-huit problèmes. Les WARNING sont hors du compte : une migration en produit des milliers. Chaque message garde son logger et sa BASE, ce qui distingue les six paliers — ils partageaient un fichier avant qu'une étape n'ouvre le sien. Mesuré : treize mégaoctets analysés en 0,09 s, soixante-dix occurrences ramenées à quinze messages. Assisted-by: Claude Opus 5
2026-08-19 07:37:40 -04:00
table.add_row(
row["label"][:38], row["detail"], row.get("severe", "")
)
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
table.styles.width = self.left_width
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
self.query_one("#head", Static).update(head_text(self.dct))
def _show(self):
[ADD] migration: announce the countdown, and cycle three panel states Every prompt now carries « ⏱5s » before the question. One that looks like it waits forever does not invite you to step away, which is the whole point of auto-run. The same clock reports the answer afterwards: it announces the wait, then says what it decided. Enter on the auto-run question already meant no; it now says so, with an explicit default instead of a deduction. And the flag is cleared before asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have answered yes on someone's behalf, and that answer commits every later one. « p » cycles three states rather than toggling one: a plain toggle freed four lines, while what takes the room is the left COLUMN — on a server log every character of width counts. All → no summary → detail only, and back. The state is named in the sub-title, which stays visible in all three: a panel that vanishes without a word reads as a broken screen. --- FR --- [ADD] migration : annoncer le compte à rebours, et trois états de panneaux Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite. Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le drapeau est effacé avant de demander : une variable héritée du shell aurait répondu « oui » à la place de quelqu'un. « p » enchaîne trois états au lieu d'en basculer un : ce qui prend la place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et l'on revient. L'état est nommé dans le sous-titre. Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
row = current_row(self.lst_row, self.index)
[ADD] migration state: colour the commands, and know when not to A list of commands in flat text blends into its own headings the moment it outgrows the screen — which is exactly when it gets read. Commands are now cyan, steps bold blue, failed ones red, and each verdict wears the colour of its meaning: 1 is amber, because « there is something to look at » is not a crash and painting it red worries for nothing. Colour is refused three times over, and each refusal has cost someone: a log file full of escape codes, a grep that finds nothing, a terminal that prints them as text. A pipe is not a screen, and NO_COLOR is honoured. The full screen goes through Text.from_ansi, which does two things: it renders the colours, and it takes the rest literally. Textual parses Rich markup, so a command containing « [1] » was being eaten as a tag and vanished from the pane without a word. That was true before the colour. --- FR --- [ADD] état de migration : colorer les commandes, et savoir s'en abstenir Une liste de commandes en texte plat se confond avec ses titres dès qu'elle dépasse l'écran — et c'est justement quand elle le dépasse qu'on la lit. Les commandes sont en cyan, les étapes en bleu gras, celles qui ont échoué en rouge, et chaque verdict porte la couleur de son sens : 1 est ambre, car « il y a quelque chose à regarder » n'est pas une panne. La couleur se refuse trois fois, et chaque refus a coûté à quelqu'un : un fichier de journal truffé de codes d'échappement, un grep qui ne trouve plus rien, un terminal qui les affiche en clair. Le plein écran passe par Text.from_ansi, qui rend les couleurs ET prend le reste au pied de la lettre : une commande contenant « [1] » était avalée comme du balisage Rich et disparaissait sans un mot. Assisted-by: Claude Opus 5
2026-08-19 05:36:49 -04:00
# `from_ansi` fait DEUX choses : il rend les couleurs, et il
# traite le reste comme du texte LITTÉRAL. Sans lui, une
# commande contenant « [1] » passait pour du balisage Rich et
# disparaissait de l'écran sans que rien ne le signale.
self.query_one("#content", Static).update(
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
Text.from_ansi(
pane_text(
self.dct, row, colour=True, show_log=self.show_log
)
)
[ADD] migration state: colour the commands, and know when not to A list of commands in flat text blends into its own headings the moment it outgrows the screen — which is exactly when it gets read. Commands are now cyan, steps bold blue, failed ones red, and each verdict wears the colour of its meaning: 1 is amber, because « there is something to look at » is not a crash and painting it red worries for nothing. Colour is refused three times over, and each refusal has cost someone: a log file full of escape codes, a grep that finds nothing, a terminal that prints them as text. A pipe is not a screen, and NO_COLOR is honoured. The full screen goes through Text.from_ansi, which does two things: it renders the colours, and it takes the rest literally. Textual parses Rich markup, so a command containing « [1] » was being eaten as a tag and vanished from the pane without a word. That was true before the colour. --- FR --- [ADD] état de migration : colorer les commandes, et savoir s'en abstenir Une liste de commandes en texte plat se confond avec ses titres dès qu'elle dépasse l'écran — et c'est justement quand elle le dépasse qu'on la lit. Les commandes sont en cyan, les étapes en bleu gras, celles qui ont échoué en rouge, et chaque verdict porte la couleur de son sens : 1 est ambre, car « il y a quelque chose à regarder » n'est pas une panne. La couleur se refuse trois fois, et chaque refus a coûté à quelqu'un : un fichier de journal truffé de codes d'échappement, un grep qui ne trouve plus rien, un terminal qui les affiche en clair. Le plein écran passe par Text.from_ansi, qui rend les couleurs ET prend le reste au pied de la lettre : une commande contenant « [1] » était avalée comme du balisage Rich et disparaissait sans un mot. Assisted-by: Claude Opus 5
2026-08-19 05:36:49 -04:00
)
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
def on_data_table_row_highlighted(self, event):
if event.data_table.id == "left" and self.lst_row:
self.index = event.cursor_row
self._show()
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
def action_refresh(self):
"""Relire le disque. La migration écrit PENDANT qu'on regarde.
L'écran s'ouvre au milieu d'une migration qui continue : sans
cela, il fallait le fermer et le rouvrir pour voir le palier
suivant.
"""
[ADD] migration: announce the countdown, and cycle three panel states Every prompt now carries « ⏱5s » before the question. One that looks like it waits forever does not invite you to step away, which is the whole point of auto-run. The same clock reports the answer afterwards: it announces the wait, then says what it decided. Enter on the auto-run question already meant no; it now says so, with an explicit default instead of a deduction. And the flag is cleared before asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have answered yes on someone's behalf, and that answer commits every later one. « p » cycles three states rather than toggling one: a plain toggle freed four lines, while what takes the room is the left COLUMN — on a server log every character of width counts. All → no summary → detail only, and back. The state is named in the sub-title, which stays visible in all three: a panel that vanishes without a word reads as a broken screen. --- FR --- [ADD] migration : annoncer le compte à rebours, et trois états de panneaux Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite. Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le drapeau est effacé avant de demander : une variable héritée du shell aurait répondu « oui » à la place de quelqu'un. « p » enchaîne trois états au lieu d'en basculer un : ce qui prend la place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et l'on revient. L'état est nommé dans le sous-titre. Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
neuf = reread(self.path)
if neuf is None:
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
return
[ADD] migration: announce the countdown, and cycle three panel states Every prompt now carries « ⏱5s » before the question. One that looks like it waits forever does not invite you to step away, which is the whole point of auto-run. The same clock reports the answer afterwards: it announces the wait, then says what it decided. Enter on the auto-run question already meant no; it now says so, with an explicit default instead of a deduction. And the flag is cleared before asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have answered yes on someone's behalf, and that answer commits every later one. « p » cycles three states rather than toggling one: a plain toggle freed four lines, while what takes the room is the left COLUMN — on a server log every character of width counts. All → no summary → detail only, and back. The state is named in the sub-title, which stays visible in all three: a panel that vanishes without a word reads as a broken screen. --- FR --- [ADD] migration : annoncer le compte à rebours, et trois états de panneaux Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite. Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le drapeau est effacé avant de demander : une variable héritée du shell aurait répondu « oui » à la place de quelqu'un. « p » enchaîne trois états au lieu d'en basculer un : ce qui prend la place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et l'on revient. L'état est nommé dans le sous-titre. Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
self.dct = neuf
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
self.lst_row = rows(self.dct)
self.index = min(self.index, max(0, len(self.lst_row) - 1))
self._fill_table()
self._show()
def action_toggle_log(self):
self.show_log = not self.show_log
self._show()
[ADD] migration: announce the countdown, and cycle three panel states Every prompt now carries « ⏱5s » before the question. One that looks like it waits forever does not invite you to step away, which is the whole point of auto-run. The same clock reports the answer afterwards: it announces the wait, then says what it decided. Enter on the auto-run question already meant no; it now says so, with an explicit default instead of a deduction. And the flag is cleared before asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have answered yes on someone's behalf, and that answer commits every later one. « p » cycles three states rather than toggling one: a plain toggle freed four lines, while what takes the room is the left COLUMN — on a server log every character of width counts. All → no summary → detail only, and back. The state is named in the sub-title, which stays visible in all three: a panel that vanishes without a word reads as a broken screen. --- FR --- [ADD] migration : annoncer le compte à rebours, et trois états de panneaux Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite. Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le drapeau est effacé avant de demander : une variable héritée du shell aurait répondu « oui » à la place de quelqu'un. « p » enchaîne trois états au lieu d'en basculer un : ce qui prend la place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et l'on revient. L'état est nommé dans le sous-titre. Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
def action_cycle_panels(self):
"""Trois états, du plus complet au plus dépouillé.
Un simple bascule ne libérait que quatre lignes. Ce qui prend
la place, c'est la COLONNE de gauche : sur un journal de
serveur, chaque caractère gagné en largeur compte. On enchaîne
donc tout → sans résumé → détail seul, et l'on revient.
L'état est écrit dans le sous-titre, qui reste visible dans les
trois : un panneau qui disparaît sans un mot se lit comme un
écran cassé.
"""
self.panel_state = (self.panel_state + 1) % len(PANELS)
apply_panels(self)
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
def action_wider(self):
[ADD] migration: announce the countdown, and cycle three panel states Every prompt now carries « ⏱5s » before the question. One that looks like it waits forever does not invite you to step away, which is the whole point of auto-run. The same clock reports the answer afterwards: it announces the wait, then says what it decided. Enter on the auto-run question already meant no; it now says so, with an explicit default instead of a deduction. And the flag is cleared before asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have answered yes on someone's behalf, and that answer commits every later one. « p » cycles three states rather than toggling one: a plain toggle freed four lines, while what takes the room is the left COLUMN — on a server log every character of width counts. All → no summary → detail only, and back. The state is named in the sub-title, which stays visible in all three: a panel that vanishes without a word reads as a broken screen. --- FR --- [ADD] migration : annoncer le compte à rebours, et trois états de panneaux Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite. Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le drapeau est effacé avant de demander : une variable héritée du shell aurait répondu « oui » à la place de quelqu'un. « p » enchaîne trois états au lieu d'en basculer un : ce qui prend la place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et l'on revient. L'état est nommé dans le sous-titre. Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
apply_width(self, self.LEFT_STEP)
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
def action_narrower(self):
[ADD] migration: announce the countdown, and cycle three panel states Every prompt now carries « ⏱5s » before the question. One that looks like it waits forever does not invite you to step away, which is the whole point of auto-run. The same clock reports the answer afterwards: it announces the wait, then says what it decided. Enter on the auto-run question already meant no; it now says so, with an explicit default instead of a deduction. And the flag is cleared before asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have answered yes on someone's behalf, and that answer commits every later one. « p » cycles three states rather than toggling one: a plain toggle freed four lines, while what takes the room is the left COLUMN — on a server log every character of width counts. All → no summary → detail only, and back. The state is named in the sub-title, which stays visible in all three: a panel that vanishes without a word reads as a broken screen. --- FR --- [ADD] migration : annoncer le compte à rebours, et trois états de panneaux Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite. Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le drapeau est effacé avant de demander : une variable héritée du shell aurait répondu « oui » à la place de quelqu'un. « p » enchaîne trois états au lieu d'en basculer un : ce qui prend la place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et l'on revient. L'état est nommé dans le sous-titre. Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
apply_width(self, -self.LEFT_STEP)
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
return StatusApp(dct, path)
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
def run_tui(dct, run_app=True, path=None):
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
"""Ouvrir l'écran. False si l'on n'a pas pu — et alors on DIT pourquoi.
Se taire ferait réafficher le rapport texte à la place de l'écran
demandé, sans rien qui distingue les deux.
"""
if not dct:
return False
if not sys.stdout.isatty():
print(f"ℹ️ {t('Not a terminal: showing the text report instead.')}")
return False
try:
from script.todo import textual_setup
except Exception:
textual_setup = None
if textual_setup and not textual_setup.ensure():
return False
try:
[ADD] migration state: l, p, r and the two resize keys Four keys, and one bug behind them. « l » separates the commands from the server log: the first says what was LAUNCHED, the second what it ANSWERED, and an update writing tens of thousands of lines buries three commands. « p » hides the summary panel, « - » and « + » move the split, « r » re-reads the disk — the migration writes while one watches, and closing the screen to see the next bump is what one ended up doing. The « missing logs » had two causes, both real. A tail was shown without saying it was one; it now names how many lines are hidden. And the first two steps run before the database is named, so their logs landed under « sans-nom » — outside the migration they belong to, invisible from the screen. They are brought back the moment the name is known, appended never overwritten. The step 4 bug was already fixed: its log held all six bumps because only print_step opened one, and the bump loop never calls it. --- FR --- [ADD] état de migration : l, p, r et les deux touches de taille Quatre touches, et un défaut derrière. « l » sépare les commandes du journal : la première dit ce qui a été LANCÉ, le second ce que cela a RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes enterre trois commandes. « p » cache le panneau de résumé, « - » et « + » déplacent la séparation, « r » relit le disque — la migration écrit pendant qu'on regarde. Les « logs manquants » avaient deux causes. On montrait une fin sans dire que c'en était une ; le nombre de lignes cachées est désormais nommé. Et les deux premières étapes tournent avant que la base ne soit nommée : leurs journaux atterrissaient sous « sans-nom », hors de la migration à laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu. Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
app = build_app(dct, path=path)
[ADD] migration: « t » shows where the migration stands A migration crosses six bumps, runs hundreds of commands and lasts hours. The journal said what had been LAUNCHED; it never said what came of it. Three hours in, one reads two hundred command lines without knowing which one failed, nor what the smoke test concluded. « v » could not carry this: it already means « view the differences » in three prompts, and a letter meaning two things is worse than a letter meaning nothing. « t » was free, and it is the same everywhere — one shared string carries both shortcuts, so no prompt can drift. Looking is not answering: the same question comes back afterwards. What was missing was the data. Failures and tool verdicts are now recorded with the step they happened in, bounded so a progression file cannot grow without end. A tool rerun after a repair keeps its LAST verdict and the count of its runs — showing both without distinction would read a repair as a lasting failure. --- FR --- [ADD] migration : « t » montre où en est la migration Une migration traverse six paliers, lance des centaines de commandes et dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que cela avait donné. Trois heures plus tard on relit deux cents lignes sans savoir laquelle a échoué, ni ce que le test de fumée a conclu. « v » ne pouvait pas porter cela : il veut déjà dire « voir les différences » dans trois invites, et une lettre qui signifie deux choses est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est le même partout — une seule chaîne porte les deux raccourcis. Regarder n'est pas répondre : la même question revient ensuite. Ce qui manquait, c'étaient les données. Les échecs et les verdicts d'outils sont désormais retenus avec l'étape où ils se sont produits. Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
except ImportError:
print(
f"ℹ️ {t('Textual is missing from this interpreter:')}"
f" {sys.executable}"
)
return False
if not run_app:
return app
app.run()
return True