Synthèse automatique
Pilote, phase par phase, la mise en production d'un serveur privé AzerothCore (World of Warcraft) : build des images Docker, avertissement des joueurs, sauvegarde du monde, remplacement du worldserver, import SQL, publication du client, tag de version et notes Discord.
Points forts
- Impose une autorisation humaine par livraison et un arrêt à la première erreur, avec annonce de chaque phase pour reprendre un ship interrompu.
- Encode des pièges spécifiques au projet : les deux images doivent être reconstruites car ac-db-import intègre les SQL dans l'image, et `docker compose down -v` est interdit car il supprime le volume de base de données.
- Dérive les notes de version uniquement de la liste des commits non livrés, au lieu de les inventer.
- Ajoute une hygiène d'exploitation utile (croissance du vhdx WSL2, accumulation de dumps de crash) en simple signalement, sans bloquer la livraison.
Limites
- Fortement couplé à un dépôt et un outillage précis (tools/bonesaw_status.py, Windows, WSL2, Docker) : difficilement réutilisable tel quel.
- `disable-model-invocation` n'est pas respecté par tous les runtimes, donc la garantie d'autorisation repose uniquement sur le texte des instructions.
- Aucune procédure de rollback n'est décrite en cas d'échec après le remplacement du worldserver.
À utiliser lorsqu'un mainteneur du serveur Bonesaw demande explicitement de livrer le travail accumulé sur le royaume en production.
À ne pas utiliser pour du développement courant, une revue de code ou un simple build qui ne doit pas toucher au royaume en production.
Analyse de sécurité
PrudenceThere is no exfiltration, obfuscation, or curl|sh payload; commands are transparent and aimed at a legitimate self-hosted server deployment. However the skill drives Docker, MySQL-as-root, WSL shutdown, and elevated diskpart against a live realm, and relies partly on runtime-specific frontmatter for its safety guarantee. That is powerful-tool usage for legitimate purposes, so caution rather than risky — though the inline root password and undefined allowed-tools are worth fixing.
- •Runs privileged infrastructure operations: docker compose build/down/up, wsl --shutdown, elevated diskpart vdisk compaction, docker builder prune — all capable of disrupting or destroying the live realm if misapplied.
- •Uses MySQL as root with a literal password on the command line (`mysql -uroot -ppassword`), which exposes credentials in process listings / shell history; a secrets file or env var would be safer.
- •No `allowed-tools` declared despite heavy shell/docker/PowerShell usage, so tool restrictions are not enforced by the manifest (the skill itself acknowledges `disable-model-invocation` is ignored by some runtimes).
- •Documented `docker compose down` step is destructive-adjacent; the text does correctly warn 'NEVER with -v', which mitigates the main data-loss risk.
- •Touches a production/live server and imports SQL migrations — high blast radius if invoked without authorization.
Exemples
/bonesaw-shipShip the unshipped commits to the live realm, then post the patch notes in the Discord thread.Deploy the creature-loot fix that's committed but not live yet — warn players first and tag the new version.name: bonesaw-ship description: Ship accumulated Bonesaw work to the live realm and to players - build, warn, save, replace worldserver, import SQL, publish the client, tag, and post Discord notes. Only ever invoked by the user typing /bonesaw-ship. disable-model-invocation: true
Ship Bonesaw
Stop unless the user asked for this
This is the only skill allowed to touch the live realm.
disable-model-invocation: true in the frontmatter enforces that under Claude
Code, but it is not honoured by every runtime - Pi, for one, reads the same
SKILL.md and ignores that key, which means the model can reach this file on its
own. So the guarantee has to live in the instructions too:
Before Phase 0, confirm the user actually asked to ship in their most recent
message - /bonesaw-ship, "ship it", "deploy", "push it live". If they did
not, say so and stop. Do not ship because the work looks finished, because the
status output says there are unshipped commits, or because a previous message
said to ship something earlier in the session. Authorisation is per-ship and
does not carry forward.
Work through the phases in order and stop at the first failure - do not carry on to a later phase to "get most of it out".
Announce which phase you are entering as you go, so an interrupted ship can be resumed from a known point.
Phase 0 - preflight
python tools/bonesaw_status.py
- Working tree must be clean. If it is not, commit first (do not stash - the stash stack is shared with other worktrees).
- Show the user the unshipped commit list. That list is the patch notes source; do not invent items that are not in it.
- If
UNSHIPPEDis empty and the server is not behind, there is nothing to ship. Say so and stop.
Glance at the disk (not every ship - roughly monthly)
Phase 1 builds an image every single ship, and WSL2 virtual disks only ever
grow. Nothing shrinks docker_data.vhdx on its own, so it creeps until C: is
full and a build fails for reasons that look nothing like disk space.
powershell -NoProfile -Command "'{0:N1} GB free on C:' -f ((Get-PSDrive C).Free/1GB); '{0:N1} GB vhdx' -f ((Get-Item $env:LOCALAPPDATA'\Docker\wsl\disk\docker_data.vhdx').Length/1GB)"
This is a look, not a gate - never block a ship on it, and do not compact
mid-ship. If C: is under ~40 GB free, or the vhdx is far larger than
docker system df says Docker is actually holding, mention it to the user and
move on. It is its own maintenance job, done between ships.
When they want it done, the sequence is a realm restart plus a compaction, so it follows the same warn/save rules as Phase 2:
docker builder prune -f # build cache; check `docker system df` first
tools/save_world.ps1 # or restart_worldserver.ps1 if humans are on
docker compose down # NEVER with -v: that deletes wow-bonesaw_ac-database
wsl --shutdown # quit Docker Desktop first or it restarts WSL
# then, elevated: diskpart /s <script> with
# select vdisk file="...\docker_data.vhdx" / attach vdisk readonly / compact vdisk / detach vdisk
docker compose up -d
On 2026-09-01 that vhdx had reached 171 GB while holding ~35 GB of data;
compaction returned 133 GB. Also worth a glance:
%LOCALAPPDATA%\Temp\wsl-crashes had 43 GB of worldserver dumps, nine of them
in three days - they accumulate on their own after every crash and nothing
cleans them up.
Decide the version: bump the patch number in tools/client-update/Bonesaw.version.
Never skip a number, including for server-only ships - the launcher and the
Discord thread both key off it.
Phase 1 - build before anything live moves
docker compose build ac-worldserver ac-db-import
Both images. This is not optional and it is the bug that keeps recurring.
ac-db-import bakes data/sql/updates/pending_db_* into the image - there is
no bind mount. Rebuilding only ac-worldserver means every new migration is
silently skipped forever: on 2026-08-22 rev_living_gear_account_keys.sql had
been sitting unapplied since 08-21 because of exactly this, so the account key
ring was dead while its code read perfectly.
If the build fails, stop here. Nothing live has been touched yet, which is the entire reason the build comes first. Fix the compile error and restart the ship.
Phase 2 - warn and save
Skip entirely if ac-worldserver is not running.
First, count REAL players online. The countdown exists to stop players taking a rollback; with nobody to roll back, five minutes of announcements to an empty realm is just five minutes. Playerbots do not count - they have no progress a restart can cost them.
docker compose exec -T ac-database mysql -uroot -ppassword -N -e "SELECT COUNT(*) FROM acore_characters.characters c JOIN acore_auth.account a ON a.id = c.account WHERE c.online = 1 AND a.username NOT LIKE 'RNDBOT%';"
Bot accounts are all named RNDBOT* (144 of 147 accounts at the time of
writing), so excluding them is what makes this count mean "humans". Do NOT use
the worldserver console's server info for this - its "Connected players"
includes bots, so it reads non-zero on an empty realm.
If that count is 0: save and restart immediately, no announcements.
powershell -NoProfile -ExecutionPolicy Bypass -File tools/save_world.ps1
On the Linux host, use python3 tools/save_world.py instead. There is no
PowerShell there, and the .ps1's documented fallback tools/worldserver_cli.py
is Windows-only too (it opens the Docker named pipe through ctypes.WinDLL), so
with SOAP disabled the entire warn/save path is unavailable and the ship silently
has no way to save at all. save_world.py attaches to the console on a pty and
leaves with ctrl-p ctrl-q. Do not replace it with a pipe into docker attach:
stdin goes straight to the worldserver console and EOF halts the server.
Note that port 7878 answering a TCP connect does NOT mean SOAP is up -- docker
proxy binds the host side regardless. Check SOAP.Enabled in
env/dist/etc/worldserver.conf.
The save still runs. It costs about three seconds and it is not a warning - it
protects world and bot state, and a stale online flag left by an earlier crash
is the one case where this count could be wrong in the dangerous direction. If
it is wrong the other way (a human logs in during the restart) they get a clean
reconnect, not a rollback.
If that count is greater than 0: the full countdown, no shortcuts.
powershell -NoProfile -ExecutionPolicy Bypass -File tools/restart_worldserver.ps1
There is no Linux port of the countdown. If humans are on and you are on the
Linux host, drive it by hand with the same helper -- python3 tools/save_world.py --command "announce <text>" --expect "" for each step of the 5/2/1/0:30/0:10
ladder, then a final plain python3 tools/save_world.py for the saveall. Do not
skip the ladder because the tool is missing.
Announces in game on a staged countdown (5 minutes, then 2, 1, 30s, 10s), then
saveall. Wait for its final line, OK: players warned ... and saved; restart is allowed. An exit code of 0 is not the same thing: on 2026-08-23 that
script was killed partway, exited 0, and had never reached the saveall - the
missing final line was the only evidence. Never use AzerothCore
server shutdown for a docker replace - it races saveall on its own timer.
Run it in the background rather than in the foreground, or a tool timeout will kill it mid-countdown, which is exactly how that happened.
Phase 3 - replace the server
docker compose up -d ac-db-import
docker compose up -d ac-worldserver
db-import first and let it complete; worldserver depends on it. Then confirm the module actually loaded and no migration errored:
docker logs --tail 200 ac-worldserver | grep -iE "living gear|error|Applying"
Re-run python tools/bonesaw_status.py - pending SQL must now read
all imported. If it does not, the db-import image did not pick the file up;
go back to Phase 1.
Phases 4-6, automated
The bookkeeping after the container replace (client publish, manifest, patch
notes, Discord, release notes, tag, push) is mechanical and was the recurring
source of forgotten steps - 0.1.79/0.1.80 shipped with no Discord post, and the
live manifest went stale twice in the 0.1.105/0.1.106 era. One command runs
the whole tail in the right order, refuses to continue on any failed step, and
tags LAST so git log --first-parent ship/<latest>..HEAD is empty when it exits:
python tools/ship_bookkeeping.py --version X.Y.Z --notes-only # then --post
python tools/ship_bookkeeping.py --version X.Y.Z --no-client --notes-only
python tools/ship_bookkeeping.py --version X.Y.Z --no-client --post
Run stage 1 (--notes-only) after Phase 3's bonesaw_status says
pending SQL: all imported. It bumps the version (exactly one patch),
builds the client if any, writes tools/patch-notes/X.Y.Z.md and stops -
nothing has been posted. The script's bullets are raw commit subjects and
only a human pass makes them read as notes, so the pass is now mandatory and
happens between the stages: edit the notes file (player-facing bullets, retest
asks for THIS ship's own changes first, no code names or file paths), then run
stage 2 (--post), which re-runs the retest check, prints the notes it is
about to publish, and only then posts to Discord, updates the GitHub release,
commits the notes, tags LAST and pushes. One set of notes goes out - the
reviewed one. The phases below document what the script does; use them as the
manual fallback when the script cannot run.
Push only to origin Raajik/wotlk-bonesaw, never to the playerbots
remote. Never force-push main.
Phase 4 - client, if client files changed (manual fallback)
Needed when anything under modules/mod-living-gear/client_addon/,
tools/client-patch/ or tools/launcher/ changed since the last ship:
git diff --stat ship/<previous>..HEAD -- modules/mod-living-gear/client_addon tools/client-patch tools/launcher
If it is empty, skip to Phase 5 - a server-only ship still bumps the version and still gets Discord notes.
Otherwise follow tools/client-update/README.md under "Ship to all players".
That file is the source of truth for these steps; do not reproduce them from
memory. The order that matters: build the launcher, create the GitHub release,
then --verify, then push the manifest. The exe is not byte-reproducible, so
rebuilding between hashing and uploading strands every player.
Push only to origin Raajik/wotlk-bonesaw, never to the playerbots
remote. Never force-push main.
Phase 5 - record the ship (tag LAST)
git commit -am "Ship Bonesaw X.Y.Z: <one line>"
git tag ship/X.Y.Z
The tag is what makes the next /bonesaw-status able to say what players have.
Without it the whole thing goes blind again. Tag after the notes commit, not
before - a tag chased by manifest/notes commits leaves --first-parent ship/<latest>..HEAD
non-empty forever and every later status reads "N commits not shipped"
(0.1.104/0.1.105/0.1.106 all needed a tag re-point for exactly this).
Phase 6 - tell people
This phase used to read "Post to Discord" with nothing to run, so it was done by hand or not at all - 0.1.79 and 0.1.80 both shipped without the notes ever reaching players, and the GitHub releases carried the placeholder "Run Bonesaw.exe." instead of a changelog. Writing notes into the chat reply is not the same as posting them. Do all three steps below.
1. Write the notes to a file
tools/patch-notes/X.Y.Z.md
Built from the Phase 0 commit list, following
.cursor/rules/discord-patch-notes.mdc: player-facing only, ASCII, plain -
bullets, grouped by theme, no file paths or spell IDs, only what actually
shipped. Extra jump is disabled - do not advertise it. Title line is
**Bonesaw X.Y.Z - patch notes**. If the client changed, end with Close Wow
and run Bonesaw.exe to update.
Every notes file ends with a "Please test these things" section, placed
just before the update line. Fixes that shipped but were never confirmed
otherwise silently age out on the tracker, and players are the only test
environment that counts. Source it live at notes-writing time, never from a
stale list. GitHub is the canonical tracker and status:awaiting-retest is
its queue of shipped-but-unconfirmed work (in-game lg_bug_report rows reach
that label via tools/bug-reports/bug_resolve.py after the sweep):
python tools/retest_list.py --raw
- First 1-4 asks about THIS ship's own changes that only a player can verify
("craft something that needs banked materials - does anything land in your
bags?"). Pass them as
-a "..."totools/retest_list.pyfor a paste-ready section, then rephrase anything that still reads like a bug title. - Then the longest-waiting open
status:awaiting-retestissues, so stale items get the spotlight instead of aging out. Drop the[Report #N]prefix - players cannot see the tracker, so issue numbers never appear in notes. - One short ask per item ("Try X, tell us if Y still happens"). Roughly 8 bullets max so the notes stay one Discord message where possible.
- If nothing is genuinely awaiting retest, omit the section. Never re-list an issue that already got a confirmed retest and was closed - the tool only lists open ones, but check before hand-adding anything.
python tools/retest_list.py --check tools/patch-notes/X.Y.Z.mdfails the ship if the section is missing or a[Report #N]leaked through.
The file is the record: it is what gets posted, what the GitHub release shows, and what the next person reads to see what a version contained. Commit it.
2. Post to Discord
python tools/post_patch_notes.py tools/patch-notes/X.Y.Z.md --dry-run
python tools/post_patch_notes.py tools/patch-notes/X.Y.Z.md
Dry-run first - it checks the notes are ASCII and previews the title-line body
plus the attachment before contacting anyone. Posting is attachment-only: the
body is just the title line and the full notes ride as a .txt attachment, so
the 2000-character message limit never splits or truncates anything. The
webhook lives in
tools/client-update/discord.webhook, which is gitignored because it is a
credential; never echo it into a log or a commit.
The script exits non-zero if Discord rejects anything, so a failed post stops the ship rather than being reported as done.
3. Put the same notes on the GitHub release
gh release edit vX.Y.Z --repo Raajik/wotlk-bonesaw --notes-file tools/patch-notes/X.Y.Z.md
Phase 4 creates the release with a placeholder because the notes may not exist yet at that point. This is where it gets the real changelog, so the release list is a readable history rather than a wall of identical one-liners.
4. Record the learnings
Append durable learnings to A:\obsidian\jeremy\wiki\Bonesaw.md - crashes, UI
rules, deploy gotchas, spell IDs, do-not-repeat mistakes. Part of shipping, not
after it.
Same file from the Linux host: /run/media/muckfup/aba/obsidian/jeremy/wiki/Bonesaw.md.
It is CRLF -- write it back as CRLF or the whole page shows as modified.
Finally
Re-run python tools/bonesaw_status.py. It should exit 0 with
"Everything committed, built, imported and published." If it does not, say
exactly what is still outstanding rather than calling the ship done.
Also confirm the notes end with the test section:
python tools/retest_list.py --check tools/patch-notes/X.Y.Z.md
Du fichier au premier essai
Le téléchargement contient exactement le contenu affiché sur cette fiche. Cette copie peut différer de la dernière version du dépôt source.
Consulter la source et ses fichiers annexesCe téléchargement contient uniquement SKILL.md. Les scripts, références, modèles et dépendances éventuels ne sont pas inclus. Récupérez les fichiers nécessaires depuis la source en conservant leur arborescence avant de poursuivre.
1. Télécharger le fichier
Télécharger SKILL.mdDepuis la racine d’un projet de test, enregistrez le fichier sous .skill-downloads/bonesaw-ship-mtqry32s/SKILL.md, ou utilisez la commande suivante. Ce dossier sert à examiner la skill avant son installation.
Commande pour macOS/Linux ou WSL : un shell POSIX et curl sont nécessaires. Sous Windows sans WSL, utilisez le bouton de téléchargement et créez le dossier manuellement.
mkdir -p '.skill-downloads/bonesaw-ship-mtqry32s' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/bonesaw-ship-mtqry32s/download' --output '.skill-downloads/bonesaw-ship-mtqry32s/SKILL.md.part' && mv '.skill-downloads/bonesaw-ship-mtqry32s/SKILL.md.part' '.skill-downloads/bonesaw-ship-mtqry32s/SKILL.md'2. Vérifier les prérequis
Lisez SKILL.md avant de l’utiliser. Si le fichier cite des scripts ou services externes, examinez-les et préparez les accès nécessaires dans votre environnement de test.
Aucune section de prérequis reconnue dans ce fichier. Cela ne signifie pas qu’il est autonome : les bibliothèques, outils et accès peuvent être cités ailleurs dans le contenu ou dans le dépôt source.
Les plateformes de la fiche sont des indications du catalogue. Les chemins ci-dessous suivent la documentation des outils ; ils ne prouvent pas que cette skill y a été exécutée. Aucune version minimale de ces outils n’est attestée pour cette fiche.
3. Charger la skill
Après vérification, copiez le dossier complet dans le projet avec l’une des commandes ci-dessous, puis ouvrez une nouvelle session de l’outil. La commande s’arrête si un dossier du même nom existe déjà : comparez les fichiers avant de le remplacer.
Claude Code
test -f '.skill-downloads/bonesaw-ship-mtqry32s/SKILL.md' && test -s '.skill-downloads/bonesaw-ship-mtqry32s/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/bonesaw-ship' && cp -R '.skill-downloads/bonesaw-ship-mtqry32s' '.claude/skills/bonesaw-ship'OpenSkills
OpenSkills doit déjà être installé. La procédure locale install/read est contrôlée avec OpenSkills 1.5.0, qui exige Node.js ≥ 20.6.0. Aucun slug du catalogue n’est résolu automatiquement.
Installation officielle d’OpenSkillsopenskills install './.skill-downloads/bonesaw-ship-mtqry32s'openskills read 'bonesaw-ship-mtqry32s'La lecture doit afficher le contenu du fichier installé et son dossier de base. Elle ne lance ni la skill ni ses dépendances.
Pour un agent qui lit AGENTS.md, synchronisez ensuite l’index. Cette commande modifie AGENTS.md : relisez le diff avant de l’utiliser.
openskills syncMode manuel ou autre outil : joignez SKILL.md à une conversation, ou ouvrez-le dans un agent capable de lire les fichiers. Ajoutez les ressources annexes nécessaires. Il n’existe pas de dossier universel reconnu par tous les outils.
4. Faire un premier essai
Utilisez une entrée courte et fictive : un endpoint d’API, un petit document ou un brief d’interface. Dans une nouvelle conversation, joignez le fichier ou donnez son chemin à l’agent, puis adaptez cette demande :
Lis .skill-downloads/bonesaw-ship-mtqry32s/SKILL.md et applique ses instructions à cet exemple : [décrire une entrée de test]. Indique les fichiers, dépendances ou accès qui manquent. Produis le résultat demandé par la skill sans inventer de données ni exécuter de script non examiné.Résultat attendu : une sortie correspondant aux instructions de la skill, ou une liste précise des prérequis manquants. Une simple confirmation de lecture ne valide pas le premier usage. Si une ressource externe est indisponible, ce parcours reste à tester.
Configurer un canal de discussion avec SecretRefs
DevOps
Configurez des canaux de discussion avec des commandes non interactives, les secrets étant uniquement des SecretRefs. Couvre les étapes de collecte, modification, réparation et vérification.
Enquêter sur les échecs CI de ClickHouse
DevOps
Investigue un échec CI de ClickHouse de bout en bout à partir d'une URL de PR ou de rapport S3. Récupère les tests échoués, classe flaky vs vraie régression, et cherche des issues/fixes existants.
Revue de PR Ansible
DevOps
Examine une PR Ansible selon le processus standardisé du projet, incluant la validation des changelogs, la couverture de tests et le suivi des commentaires.