rp-telemetry
Capturez la télémétrie par exécution qui nous indique ce qu'il faut améliorer dans les skills et dans l'outillage Wix sous-jacent. Cette skill reste active aux côtés des skills de migration pour l'intégralité de l'exécution ; les skills de migration elles-mêmes ne sont pas modifiées et ne savent rien de la télémétrie.
La règle unique qui gouverne tout ce que vous enregistrez
Observation, pas diagnostic. Chaque champ enregistre ce qui a été observé — ce qui s'est passé, ce qui était attendu, ce qui s'est réellement produit, où et à quelle fréquence. Ne jamais enregistrer une cause profonde, une correction, une recommandation ou un contournement présenté comme une solution. Il n'y a délibérément aucun champ pour eux ; le schéma rejette les champs inconnus. La cause profonde est dérivée plus tard, au moment de l'examen, par un agent examinateur avec le contexte complet — pas affirmée par vous sur le moment.
Trois règles de discipline s'appliquent à chaque champ en texte libre (what_happened, expected, actual) :
- Types, pas instances. Référencez les types et classes d'entités, jamais les données client spécifiques.
entity_type: "product"— jamais le nom d'un produit, le SKU, le prix ou le contenu du corps. - Secret-safe. Aucune credential, token, URL, valeur de config ou contenu de fichier — l'enregistreur exécute également un nettoyage mécanique en dernière ligne de défense, mais ne vous y fiez pas.
- Court et structurel. Une phrase ou deux, max 400 caractères. Préférez exposer expected-vs-actual plutôt que de narrer. Les champs codés portent la structure.
- Pas de narration de remédiation. « La lib canonique a été corrigée et resynchronisée » est une histoire de correction, pas une observation — même si c'est vrai (backports en mode dev). Enregistrez ce qui a été observé comme fonctionnant (« ajouter fieldsets=FULL a retourné le champ ; la recherche a réussi ») et laissez la réparation au journal d'exécution.
L'enregistreur
Toute la télémétrie est persistée via l'enregistreur fourni — ne jamais rédiger ou éditer manuellement run-telemetry.json ou les fichiers telemetry/. Exécutez-le depuis ce répertoire de ressources (voir CONVENTIONS.md), en le pointant vers le projet de migration actif :
node scripts/rp-telemetry.js <command> [...] --project <abs path to migrations/<project>>
L'enregistreur est propriétaire de tout ce qui est mécanique : validation de schéma (il rejette les énumérations invalides et les champs inconnus — corrigez et réessayez, ne devinez jamais), horodatages, repliement d'événements par classe, le modèle run/attempt/session resume, le nettoyage de confidentialité et la porte de bien-formedness à la finalisation. Il imprime un résultat JSON par appel ; {"ok":false,...} liste exactement ce qu'il faut corriger.
| Appel | Quand |
|---|---|
start '<dims-json>' |
Au début de l'exécution — avant toute autre étape du pipeline. Reprend automatiquement une exécution non finalisée. |
dims '<dims-json>' |
Chaque fois qu'une dimension devient connue en cours d'exécution (version de la plateforme et extensions après la découverte, site_id après le provisionnement). |
stage start <stage> / stage end <stage> --outcome <outcome> |
À chaque limite de stage. Résultats : passed, halted, failed, skipped. |
meter [--api-ms N --model-ms N --script-ms N --input-tokens N …] |
Latences mesurées/décomptes de tokens pour un stage. Appelez-le chaque fois que vous avez des chiffres réels ; voir « Metering » ci-dessous. |
wait start [--halt <subtype> --skill <s> [--what '<text>']] / wait end |
Le moment où l'exécution s'arrête pour l'utilisateur, et le moment où elle reprend. C'est ainsi que la latence utilisateur reste hors de active_ms — ne jamais estimer le temps écoulé vous-même. Passez --halt pour un arrêt needs-user et l'enregistreur émet l'événement halt_needs_user appairé pour vous. |
record '<event-json>' |
Le moment où quelque chose d'observable se produit (taxonomie ci-dessous). |
finalize '<rollup-json>' |
Quand l'exécution atteint un état terminal. |
rebuild [--attempt <n>] [--push] |
Uniquement sur demande, pour réassembler le document de signal d'une exécution passée à partir de son journal archivé après une correction d'enregistreur — jamais pendant une exécution. --push ré-émet l'exécution reconstruite au sink BI (remplissage après une panne ; idempotent — l'examinateur déduplique au moment de la requête). |
status |
Pour vous orienter après une reprise. |
Cycle de vie de l'exécution
-
startcomme premier acte de télémétrie de l'exécution, avec toutes les dimensions déjà connues :node scripts/rp-telemetry.js start '{"source_platform":"wordpress", "source_site_url":"https://client-site.example","source_acquisition":"public_storefront", "delivery_mode":"management","destination_strategy":"new_site", "runtime_env":{"agent_runtime":"claude-code","model":"<model id>"}}' --project <dir>startsur un projet avec une exécution non finalisée la reprend (même exécution, même tentative, une session de plus) et ferme tout intervalle wait ouvert.startaprès une exécution finalisée ouvre la tentative suivante. Ne jamais essayer de gérer vous-même l'identité de l'exécution.source_acquisitionest un ensemble ouvert de tokens de classe, mais réutilisez un token établi (admin_api,public_storefront,public_content,file_export) plutôt que de frapper un synonyme — le repliement inter-exécution dépend de tokens stables.La provenance des skills est auto-estampillée — vous ne la passez pas. À
start, l'enregistreur appose deux identifiants sur l'exécution pour que chaque enregistrement puisse être retraçable aux skills qui l'ont produit :skills_version— le label de version sémantique du bundle (depuisVERSION). Grossier et bump manuel par conception ; c'est la clé group-by/order-by qui lie les problèmes à une ligne de version (« tous les problèmes sur 1.2.x », « régression depuis 1.1.0 »).skills_commit— le commit source exact à partir duquel le bundle a été construit : l'instantané vendorisé précis au sein d'une version. De nombreux commits sortent sous unskills_versionbump manuel, donc une exécution rapportant1.0.0est autrement inattribuable au changement qui a produit son problème. L'enregistreur le résout, en ordre d'autorité, à partir dusourceCommitestampillé dans.publish-manifest.jsonà la publication (la seule source qui fonctionne dans un runtime partenaire, et la seule correcte une fois que le bundle est vendorisé danswix/skills), sinon ungit rev-parse --short HEADmode dev du checkout source, sinonnull.
Les deux peuvent être remplacés en passant
skills_version/skills_commitdans les dimsstart(un runtime avec une meilleure provenance que l'enregistreur peut déduire), mais normalement vous les laissez auto-résoudre. -
Les limites de stage au fur et à mesure que le pipeline avance. Mappez les étapes de l'orchestrateur aux stages comme ceci :
Stage Couvre configTout avant la découverte : résolution du projet, fichiers de config, collecte d'entrée en amont discoveryDécouverte source ( rp-discovery+ adaptateur source)mcp_gateLa porte de prérequis Wix MCP entre la découverte et le mapping mappingrp-mapperproduisant le plan de mappingmapping_reviewLe point de contrôle d'examen du mapping (face à l'utilisateur) setup_discoveryrp-setup-discoverycodegenrp-import-codegenapproval_gateLa porte d'approbation du plan d'exécution (face à l'utilisateur) setup_provisioningCréation de site, installations d'app, collections — rp-execute-setupstorefront_buildLa compilation + publication wix-headlessen modewebsite;skippeden modemanagementextractExtraction source sur disque, avant toute écriture ( rp-execute-import)importLes écritures d'import ( rp-execute-import)finishVérifications spot-check, rapport d'achèvement, remise Les stages se lient au moment où le travail s'exécute réellement, pas à leur ordre canonique. Si l'orchestration exécute l'extraction en amont (ex. extract + dry-run avant la porte d'approbation, à l'intérieur de la phase codegen), fermez le stage ouvert, encadrez l'extraction dans son propre stage
extract, puis rouvrez — un stage peut être entré plus d'une fois, et sonactive_mssomme les entrées. Réserver une extraction réelle danscodegen(et laisserextractcomme un stage token de quelques millisecondes) est exactement la mal-attribution que le stageextractexiste pour prévenir.Quand le stage
discoveryse termine, toujours estampillez ce qu'il a appris :dims '{"source_platform_version":"...","source_extensions":["..."]}'— passez[]explicitement quand la découverte n'a trouvé aucune extension ; un null laissé derrière est signalé danstelemetry_healthcommesource_extensions_null_after_discovery. -
wait start/wait endautour de chaque arrêt needs-user : demandes de credential, le point de contrôle d'examen du mapping, la porte d'approbation, tout arrêt-to-needs-user. Passez la classe d'arrêt sur le même appel —wait start --halt missing_input --skill rp-mapper --what "run halted at the mapping-review checkpoint awaiting approval"— et l'enregistreur émet l'événementhalt_needs_userappairé mécaniquement ; un wait bloqué sans événement halt dans son stage est signalé danstelemetry_health(wait_without_halt_event:<stage>). Si la session est sur le point de se terminer sur un arrêt, laissez le wait ouvert — lestartde la reprise le ferme, donc la latence utilisateur du jour au lendemain atterrit danswaiting_msoù il appartient. Si vous reprenez le travail réel pendant qu'un wait est ouvert (ex. enquêter quelque chose avant que l'utilisateur ait répondu),wait endd'abord etwait startà nouveau quand vous retournez à l'attente — le travail actif ne doit jamais être compté comme attente. -
recordles événements au fur et à mesure qu'ils se produisent (section suivante). Enregistrez au moment présent, pas rétrospectivement — l'improvisation et les arrêts ne sont sûrement connaissables que quand ils se produisent. -
finalizeune seule fois, à un état terminal, avec le rollup :node scripts/rp-telemetry.js finalize '{"terminal_state":"completed", "volumes":[{"entity_type":"product","target":"native","target_surface":"stores/v3", "discovered":142,"planned":142,"attempted":142,"succeeded":139,"failed":3, "skipped":0,"already_imported":0}], "verification":[{"subject":"product","method":"query_back","checked":10,"passed":10,"failed":0}], "operator_acceptance":"accepted"}' --project <dir>volumesproviennent des artefacts d'exécution (manifeste, journal d'audit, crosswalk) :plannedettarget/target_surfacedu plan approuvé ;already_importedest le scope crosswalk-skipped d'une tentative antérieure — ne jamais le replier dansskipped.- Inclure une ligne de volume pour chaque type d'entité que la découverte a trouvé en usage — y compris les types exclus par décision utilisateur ou sans cible Wix propre (
discovered: N, skipped: N,target: nonelà où aucune cible n'existe,planned0 ou null). « Ce que nous ne pouvons pas ou avons choisi de ne pas importer » doit être l'arithmétique de la couche signal, jamais une fouille en profondeur — les lignes uniquement pour les types importés effacent silencieusement la moitié exclue du plan approuvé. verificationrend les spot-checks de l'étape finish comptables.checked: 0est un honnête « écrit mais non vérifié » — rapportez-le plutôt que de le dissimuler.- Ne pas finaliser un arrêt que vous vous attendez que l'utilisateur reprenne — laissez l'exécution non finalisée avec le wait ouvert. Finalisez avec
halted_needs_useruniquement quand l'exécution est véritablement fermée dans un état bloqué. Un utilisateur qui refuse le plan estabandoned_by_user(plus un événementuser_decisionavecsubtype: declined), jamaishalted_needs_useroufailed.
Metering — diviser active_ms en où le temps s'est réellement dépensé
active_ms seul ne peut pas localiser un goulot : c'est le wall-clock entre deux limites de stage, fusionnant le raisonnement du modèle + l'exécution du sous-processus + la latence de l'API distante + le temps de réparation de défaut en un seul nombre. meter le divise, et le coût est dérivé des décomptes de tokens.
# un script généré rapportant son propre travail mesuré
node scripts/rp-telemetry.js meter --api-ms 250000 --api-calls 73 --api-retries 2 --script-ms 1200 --project <dir>
# le runtime de l'agent rapportant sa propre utilisation pour le stage qu'il vient de terminer
node scripts/rp-telemetry.js meter --model-ms 42000 --input-tokens 8000 --output-tokens 1500 --cache-read-tokens 120000 --project <dir>
Dérivez la moitié API mécaniquement — ne la comptez pas manuellement. Après qu'un import généré s'exécute :
node scripts/meter-from-audit.js --project <migration dir> --stage import # --dry-run pour prévisualiser
Il lit logs/import-audit.ndjson et émet api_ms / api_calls / api_retries mesuré.
Il existe parce qu'une écriture en masse enregistre une ligne d'audit par item, chacune portant la latence totale du batch — en sommant les lignes multiplie la latence d'un appel par son décompte d'items, ce qui sur une exécution réelle a rapporté 1 050 992 ms de temps API à l'intérieur d'un stage qui n'existait que pour 346 505 ms. L'assistant replie les lignes par distinct (runId, endpoint, batch, latencyMs), donc un batch compte une fois tandis que les appels véritablement distincts comptent chacun.
Champs : durées model_ms / api_ms / script_ms ; décomptes input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, api_calls, api_retries. Par défaut le stage ouvert ; --stage <stage> cible un autre. Les appels répétés s'accumulent, donc chaque invocation de script et chaque réintrée de stage rapportent indépendamment.
Les règles qui gardent les chiffres fiables :
- Mesurez, ne devinez jamais. Chaque champ est un nombre quelque chose a réellement observé — le temps écoulé d'un script, la somme
latencyMsd'un journal d'audit, l'utilisation rapportée du runtime. Si vous n'avez pas de mesure, omettez le champ ; le lacune est rapporté commeunattributed_mset signalé, ce qui est bien plus utile qu'une devinette. - Ne mettez pas en mètre le même intervalle deux fois. Les mètres se somment, donc re-rapporter le temps API d'un stage après une reprise le compte deux fois. L'enregistreur signale
stage_over_attributed:<stage>quand le temps attribué dépasse le temps écoulé, parce queunattributed_msse pince à zéro et sinon le cacherait. - Le coût est dérivé, jamais enregistré. Passez
model_pricing_snapshotdans les dimsstart({ "<model>": { "input_per_mtok": 15, "output_per_mtok": 75, "cache_read_per_mtok": 1.5 } }) et le rollup calculecost.estimated_cost_usd. Un chiffre en dollars stocké se trompe silencieusement quand les prix catalogue changent ; les tokens plus un instantané daté restent recompilables. Sans un instantané, le coût estnullaveccost_basis: "no_pricing_snapshot"— jamais un nombre fabriqué. contained_recoveryest dérivé, pas auto-rapporté. Touterroroupipeline_defectdans un stage le marque, parce qu'un stage qui a dépensé son temps à déboguer est celui qu'un agent est le moins susceptible de se rappeler de signaler.timing.stages_with_recoveryles énumère, donc une exécution propre et une chasse aux bogues sont distinguables au lieu que les deux lisent comme « c'est ce que le stage coûte ».
Le rollup's timing.agentic_ms vs timing.deterministic_ms (et agentic_share) est le nombre qui montre si le déplacement du travail vers du code déterministe paie. agentic_share est null quand rien n'a été mis en mètre plutôt que 0, ce qui lirait faussement comme une exécution entièrement déterministe.
Collecter operator_acceptance
À la remise de finition, posez à l'opérateur une simple question : le résultat migré leur semble-t-il correct — accepted, rework_needed ou rejected ? Enregistrez sa réponse dans finalize. Si l'exécution n'atteint jamais finish ou qu'il ne répond pas, elle reste unknown.
C'est le seul champ séparant « complété et bon » de « complété et inutilisable » — posez la question, mais ne pressez ni n'interprétez jamais ; leur verdict tel que donné, grossier par conception.
Taxonomie des événements — quand enregistrer quoi
Les événements sont repliés par classe de problème par l'enregistreur (même type + stage + entity type + surfaces API + app + code erreur + subtype se replient en un événement avec un count), donc enregistrez chaque classe d'occurrence une fois et passez count quand vous avez observé beaucoup à la fois — ex. un script généré rapportant 4 000 défaillances d'écriture identiques est un appel record avec "count": 4000. Les problèmes distincts sont des subtypes ou codes erreur distincts, pas des counts plus gros.
Chaque événement a besoin de : event_type, stage (par défaut le stage ouvert), skill (la ressource rp-* active, ex. "rp-mapper"), severity (blocking | degraded | cosmetic | info), et what_happened. Ajoutez entity_type, wix_api_surface (ex. stores/v3), source_api_surface (classe d'endpoint comme wp/v2/posts — jamais une URL), wix_app_id, et error_code chaque fois qu'ils s'appliquent — ce sont les clés group-by inter-exécutions.
-
halt_needs_user— l'exécution s'est arrêtée à un état needs-user défini. Préférez la forme mécanique —wait start --halt <subtype> …l'émet pour vous (étape du cycle de vie 3) ; enregistrez-la vous-même uniquement pour un arrêt sans intervalle wait.subtype:missing_input(entrée/credential requise manquante/invalide) |manual_only(étape véritablement manuelle, pas d'API) |systemic_failure(défaillance systémique ou risque de perte de données). -
manual_action_required— le plan d'exécution a signalé quelque chose comme « ne peut pas être fait, nécessite action manuelle » (ex. une mise à niveau de plan de stockage), que l'exécution s'y soit arrêtée ou non.subtype:plan_or_billing|dashboard_only|external_dependency|other. -
error— une erreur API ou script, y compris les erreurs récupérées. Portezerror_code(obligatoire — c'est le discriminateur ; pas de subtype),retry_count, etrecovered. Enregistrez un événement par chaîne de retry résolue, après que le résultat soit connu — lerecoveredfinal, leretry_counttotal — jamais un appel par tentative (les appels par-tentative se replient en une classe, et les champs de la première tentative échouée enterreraient la récupération). Quand la récupération a nécessité un changement (pas juste un retry),actualdoit enregistrer quel changement l'a fait réussir, comme une observation : « réessayé avec la description en texte brut au lieu de HTML ; l'écriture a réussi » — jamais « le correctif est X ». Si ce changement s'est écarter du chemin documenté — y compris un changement à une commande ou valeur planifiée, comme simplifier une entrée que le plan a spécifiée — émettez également un événementskill_coverage_gapappairé (undocumented_workaround). -
fidelity_loss— la migration a techniquement procédé mais a perdu quelque chose.subtype:dropped_field|unverified_enum|no_target(entité source sans cible Wix propre) |coerced_value. Pour une perte déclenchée par des enregistrements spécifiques, mettez en ligne la forme assainie de l'enregistrement offensant dansobserved_shapes— noms et types de champs et quel champ a été supprimé/coercé, jamais les valeurs. -
api_gap— une capacité API Wix manquante ou insuffisante.subtype:missing_api|missing_capability(l'API existe mais ne peut pas exprimer l'opération) |internal_only(la capacité existe mais n'est pas publiquement exposée) |other. Portezwix_api_surface(obligatoire) eterror_code— cette signature est appairée centralement inter-exécutions. Ne jamais écrire dans aucun fichier de carnet ; l'événement est l'obligation entière par exécution. -
skill_coverage_gap— l'auto-rapport permanent, et le signal de plus haute valeur pour améliorer les skills. Chaque fois que vous agissez au-delà de ce que la skill active vous a explicitement dit de faire — vous devinez une valeur, contournez une instruction manquante, résolvez une ambiguïté par jugement, ou gérez un cas que la skill ne couvre pas — enregistrez-le à ce moment.subtype:guessed_value|undocumented_workaround|ambiguous_instruction|path_not_covered. Décrivez la situation et l'action entreprise comme une observation : « la skill n'a pas spécifié quelle valeur enum mappe à X, donc une valeur a été choisie pour procéder » — jamais « la skill devrait ajouter Y ». Si vous êtes incertain de si quelque chose compte comme improvisation, c'est le cas — enregistrez-le. -
user_decision— la réponse de l'utilisateur à chaque point de contrôle ou fourche défini : le point de contrôle d'examen du mapping, la porte d'approbation, et les fourches d'intake (anonymisation des commentaires, mode de livraison, accessibilité des médias, …).subtype:accepted|declined|deferred|amended;decision_point(obligatoire) nomme la fourche, ex.mapping_review,approval_gate,comments_anonymization. Enregistrez la décision, pas le raisonnement verbatim de l'utilisateur. Une acceptationamendeddont le changement a exposé un problème de mapping surfacise aussi ce problème commefidelity_lossouskill_coverage_gap. Undeclinedà un point de contrôle terminal s'apparie avecterminal_state: abandoned_by_user. -
pipeline_defect— notre propre machine de migration s'est mal comportée : pas une erreur API Wix, pas un défaut source, pas votre improvisation.subtype:state_inconsistency(deux artefacts d'état du pipeline sont en désaccord) |ordering_violation(une étape a couru avant que la sortie de son prérequis n'existe) |record_defect(un enregistrement/journal du pipeline est mal formé ou trompeur comme preuve) |other. Nommez les artefacts et leurs états danswhat_happened— noms et états, jamais le contenu.
evidence_refs — rare, pas routinier
Rédigez chaque événement pour être autosuffisant : champs codés + texte libre borné + observed_shapes devrait laisser un examinateur le trier sans ouvrir aucun artefact. Ajoutez evidence_refs ({"artifact":"execution-log.md","locator":"## Import"}) uniquement pour l'événement exceptionnel dont le signal ne peut vraiment pas porter l'observation complète. Utilisez les chemins relatifs à la racine du projet et préférez les titres de section comme localisateurs (les plages de lignes se cassent quand les fichiers se régénèrent). Ne jamais référencer des fichiers config porteurs de secrets.
Ce qu'il ne faut jamais faire
- Ne jamais rédiger, éditer ou relire manuellement
run-telemetry.jsonou les fichierstelemetry/— l'enregistreur ajoute ; vous ne l'appelez que. - Ne jamais enregistrer les valeurs des données client, noms, URLs au-delà de l'origine source enregistrée, ou les secrets — dans aucun champ, y compris les formes et localisateurs.
- Ne jamais enregistrer un correctif, une cause profonde ou une recommandation — uniquement les observations.
- Ne jamais estimer les durées — l'horodatage provient des appels limite
stage/wait. - Ne jamais sauter un appel rejeté : corrigez les champs énumérés et réessayez. Les rejets sont comptabilisés contre la santé de la télémétrie de toute façon.
- Ne jamais créer ou mettre à jour les fichiers de carnet d'amélioration/demande de fonctionnalité pendant une exécution — ceux-ci sont maintenus centralement à partir de la télémétrie de nombreuses exécutions, pas par exécution.
- Ne jamais supprimer ou réduire les artefacts du projet pour des raisons de télémétrie — la capture est une vue non-destructive ; la taille n'est pas une raison de laisser tomber quoi que ce soit.