ticket-driver
Objectif
Traiter un ticket Redmine (fix de revue OU feature) de la prise en main jusqu'à la recette, selon le mode du projet (auto, propose, signal). Respecter la convention du projet et sa gate de validation.
Modes (par projet, dans state/config.json → projects.<name>.mode)
- auto : corriger + valider + push + report + journal Redmine, sans validation humaine.
- propose (défaut) : préparer la proposition (worktree + fix + gate +
state/proposals/<projet>_<mr>.md), envoyer l'approbation Telegram (2 boutons ✅/❌), NE PAS pousser/reporter tant que non approuvé. - signal : notifier seulement (message info Telegram, pas de fix ni de bouton).
Routage projet (OBLIGATOIRE — à faire en premier)
/home/y-note/openimis*→ skill openimis/home/y-note/OrangeMoney/MobileInvoice→ skill mi/home/y-note/flownote→ skill flownote- Autre → lire
CLAUDE.md/ convention du repo.
Workflow (mode propose — cœur)
- Contexte : le
fetchdétecte la revue (MR/PR/ticket) ; le retour portebody,url,title,discussion_id,key,sub,sub_id. propose.py(déterministe, rapide) : écritstate/proposals/<projet>_<mr>.md(PROJECT, MR, TITLE, URL, DISCUSSION, MARK_KEY, MARK_SUB, MARK_ID, SUMMARY) et envoie l'approbation Telegram (tg.py send-approval, boutons ✅/❌). Marque l'itemproposed. N'implémente pas, ne pousse pas, ne reporte pas.- Approbation (humain sur Telegram) :
- ✅ →
scripts/ticket-driver-approve.sh <projet> <mr>: lancecodex execqui implémente le fix (worktree de la branche source), valide (gate du projet), pousse, puis reply + résolution + synthèse et journal Redmine ; marque (mark-key). Statutapproved. - ❌ → statut
rejected, aucune action.
- ✅ →
- Marquer :
mark-key --project <p> --key <key> --sub <sub> --id <id>(dédup). - Escalade : retour non-actionnable/ambigu/risqué → ne pas répondre/résoudre ; le résumer, le marquer comme traité.
Fichiers d'état
state/projects.json: curseurs des notes/journaux traités (dédup).state/approvals.json: statutproposed/approved/rejected/signaledpar item.state/proposals/<projet>_<mr>.md: proposition en attente d'approbation.
Commandes utiles
- Fetch :
scripts/rfbot.py fetch --project <p> --out /tmp/items.json --pretty - Telegram :
scripts/tg.py send-approval|send-info|poll - Approbation :
scripts/ticket-driver-approve.sh <projet> <mr> - Report :
scripts/rfbot.py report --project <p> --target <mr:33|ticket:37862> --action <reply|resolve|summary|journal> - Modes :
scripts/rfbot.py modes(lister) ;scripts/rfbot.py set-mode --project <p> --mode <auto|propose|signal>(changer). - Config :
state/config.json(projects.<name>.mode,telegram.*,redmine.*).
Sprint (nouveaux tickets Redmine assignés)
- Détection :
rfbot.py fetchajoute une sourcesprint_ticket: version sprint courant =DH-OpenIMIS<AAAA><MM><NN>ouverte avec ladue_datela plus proche ≥ aujourd'hui (dédupliquée parversion.id), puis issuesassigned_to_id=redmine.me_iddans cette version (projets routés viaredmine.sprint_routes:199→openimis,170→mi,209→flownote). - Item :
kind=sprint_ticket,key=sprint_ticket_<id>,sub=processed_tickets,sub_id=ticket_id, portantsubject,body,tracker,url,redmine_project_id. - Propose : pour un
sprint_ticket, le modèle rédige un plan d'implémentation clair (contexte, étapes, fichiers, PR attendue, tests) et l'envoie sur Telegram ; la proposition est écrite dansstate/proposals/<projet>_<ticket_id>.md. - Approbation : ✅ → le skill projet implémente la feature (worktree
feature-<n>, validation, push, PR, journal Redmine recette) ; ❌ → rejeté.
Validation
- La gate de validation est celle du skill projet (
mi/openimis/flownote) : lire ce skill et appliquer sa validation (conteneur MI, uv/pytest flownote, phing/vitest openimis…). - Appliquer de façon ciblée (fichiers touchés) et fail-fast (
timeoutvia configvalidation.timeout_seconds, défaut 900 s) : si l'environnement est cassé (conteneur instable, donnée DB absente), rapporter la cause et s'arrêter — pas de retry, pas de hang, pas de push en masse.
Fichiers d'état
state/projects.json: curseurs des notes/journaux traités (dédup).state/approvals.json: statutproposed/approved/rejected/signaledpar item.state/proposals/<projet>_<mr>.md: proposition en attente d'approbation.
Commandes utiles
- Fetch :
scripts/rfbot.py fetch --project <p> --out /tmp/items.json --pretty - Telegram :
scripts/tg.py send-approval|send-info|poll - Approbation :
scripts/ticket-driver-approve.sh <projet> <mr> - Report :
scripts/rfbot.py report --project <p> --target <mr:33|ticket:37862> --action <reply|resolve|summary|journal> - Modes :
scripts/rfbot.py modes(lister) ;scripts/rfbot.py set-mode --project <p> --mode <auto|propose|signal>(changer). - Config :
state/config.json(projects.<name>.mode,telegram.*,redmine.*).
Sprint (nouveaux tickets Redmine assignés)
- Détection :
rfbot.py fetchajoute une sourcesprint_ticket: version sprint courant =DH-OpenIMIS<AAAA><MM><NN>ouverte avec ladue_datela plus proche ≥ aujourd'hui (dédupliquée parversion.id), puis issuesassigned_to_id=redmine.me_iddans cette version (projets routés viaredmine.sprint_routes:199→openimis,170→mi,209→flownote). - Item :
kind=sprint_ticket,key=sprint_ticket_<id>,sub=processed_tickets,sub_id=ticket_id, portantsubject,body,tracker,url,redmine_project_id. - Propose : pour un
sprint_ticket, le modèle rédige un plan d'implémentation clair (contexte, étapes, fichiers, PR attendue, tests) et l'envoie sur Telegram ; la proposition est écrite dansstate/proposals/<projet>_<ticket_id>.md. - Approbation : ✅ → le skill projet implémente la feature (worktree
feature-<n>, validation, push, PR, journal Redmine recette) ; ❌ → rejeté.
Validation (ciblée + fail-fast)
- Ciblée : n'exécuter que les tests/analyses des fichiers touchés (test PHP/phpcs/phpstan pour le contrôleur modifié ; vitest/tsc/lint pour le frontend) — ne PAS lancer
phing full-buildcomplet (lent). Les commandes sont dansstate/config.json→projects.<name>.validation. - Fail-fast :
timeoutglobal (configvalidation.timeout_seconds, défaut 900 s) sur les runscodex exec. Si conteneur instable ou donnée DB absente (ex.PaiementMethod 'MTNMoney'), rapporter la cause exacte et s'arrêter — pas de retry, pas de hang, pas de push en masse.