Synthèse automatique
Vérifie le comportement réel d'une PR ou d'une branche avant merge en lançant l'application dans un slot de dev isolé, en parcourant le flux modifié de bout en bout (API + navigateur headless) et en consignant une preuve pour chaque critère d'acceptation.
Points forts
- Impose de figer les critères d'acceptation avant de toucher à l'environnement, chacun devant finir en ✅/❌ accompagné d'une preuve.
- Isole la QA dans un slot dédié (worktree détaché, ports calculés selon la formule port + slot×10) sans perturber les slots déjà lancés par l'utilisateur.
- Documente un repli précis quand `./dev up` échoue à cause d'un conteneur périphérique en mauvaise santé : démarrage natif de l'app contre l'infra saine.
- Prévoit un teardown explicite et des symlinks d'env plutôt que des copies, pour laisser la machine dans son état initial.
Limites
- Très couplé au dépôt ayunis-core : chemins absolus, CLI `./dev`, formule de ports et filtres pnpm ne se transposent pas ailleurs.
- Le SKILL.md fourni est tronqué : le seed, le pilotage du navigateur headless et le teardown sont annoncés mais non détaillés.
- Dépend de plusieurs autres skills (worktree, dev-environment, e2e) et d'outils authentifiés (gh, CLI linear), sans vérifier leur disponibilité.
À utiliser quand il faut confirmer qu'une PR ou une branche se comporte vraiment comme prévu dans l'application qui tourne, juste avant le merge.
À ne pas utiliser pour juger la qualité du diff (c'est le rôle de /code-review) ni dans un dépôt dépourvu du tooling `./dev` et des slots de dev d'ayunis-core.
Analyse de sécurité
PrudenceThe skill is a legitimate pre-merge QA workflow: it uses bash, Docker, curl and headless-browser automation for a real engineering purpose and explicitly constrains destructive actions (no volume removal, no killing unowned processes, no touching existing slots, targeted `rm`/`restore`/`worktree remove`). Risk is limited to handling container-sourced credentials into a gitignored env file and running Chrome unsandboxed, both standard for local QA, so it rates caution rather than risky. Nothing exfiltrates data, decodes obfuscated payloads, or disables safety mechanisms.
- •Handles live credentials: reads MinIO/Redis passwords out of running containers via `docker inspect` and appends them to a gitignored `.env.dev`; mitigated by explicit 'never echo secrets to the terminal' guidance and local-only file targets (no network egress).
- •Launches headless Chrome with `--no-sandbox`, disabling the browser sandbox — common for CI/QA but a real weakening of isolation when driving arbitrary app content.
- •Starts background backend/frontend dev processes, Docker Compose stacks, and Puppeteer automation; all bounded by explicit guardrails (no kill/pkill of unowned processes, no `down -v`/`volume rm`/`system prune`, no touching pre-existing slots).
- •Copies secrets by symlinking the main checkout's `.env` files into a temp worktree; acceptable for local dev but expands the surface where provider API keys live on disk.
- •No prompt-injection or instruction-override attempts observed in the content.
- •Relies on docker compose, git worktree, curl, and a headless Chrome launched with --no-sandbox — powerful tooling, though used for a legitimate local dev/QA workflow.
- •Symlinks real .env files (provider API keys) into a throwaway worktree and recovers MinIO/Redis credentials via `docker inspect`; guidance explicitly says not to echo secrets to the terminal, but the secret-handling surface is non-trivial.
- •Writes and later deletes `.env.dev` and runs migrations/seeds against a local dev database; scope is bounded to isolated dev slots with explicit guardrails against `down -v`/prune/kill.
Exemples
QA AYC-2 before merge — spin up an isolated slot, drive the changed flow end-to-end and give me a ✅/❌ checklist with evidence for each acceptance criterion.Verify PR #412 actually behaves as described in its body in the running app, not just in the unit tests, then tear everything down.Check that the behaviour of my current branch works in the real app: isolate a dev slot, exercise the changed endpoints and UI flow, and leave the machine as you found it.name: qa description: Behaviour QA of a PR/branch before merge in ayunis-core — spin up an isolated dev slot, seed, drive the changed flow end-to-end (API + headless browser), assert acceptance criteria with evidence, then tear everything down. Use when asked to "QA", "verify a PR/branch before merge", "check the behaviour works", or to confirm a change works in the real running app (not just tests).
Pre-merge QA
Verify that a PR actually behaves to spec by exercising it in the running app, then leave the machine exactly as it was. This is behaviour verification, not code review — pair it with /code-review for the diff.
Scripted regression coverage lives in ayunis-core-e2e/ (see the e2e skill). When an applicable spec exists or the repository's Proportional Workflow requires browser coverage, run the focused spec first (pnpm --filter ayunis-core-e2e test --grep "<area>", needs an --e2e stack); a green spec is evidence for its criterion. Spend manual QA effort on what lower-level and E2E coverage do not prove: the PR-specific behaviours, visuals, and edge cases. Missing E2E coverage is a definition-of-done gap only when the change's classification or failure modes require it.
Input
A ticket ID (e.g. AYC-2), a PR number/URL, or a branch name. If none given, use the current branch. Optionally, explicit acceptance criteria — otherwise derive them (below).
0. Establish acceptance criteria FIRST
You can't verify "it works" without knowing what "works" means. Get the criteria before touching the environment:
- Ticket —
linear issue view <ID> --json(the description's "Solution"/acceptance section). - PR —
gh pr view <n> --json title,body. - If still ambiguous, ask the user for the 1–3 concrete behaviours to confirm.
Write them down as a checklist. Every one must end the run marked ✅/❌ with evidence.
1. Worktree the branch
Use the worktree skill. Base the worktree on the PR's branch (not a new one):
git fetch origin
# --detach: QA is read-only, and it avoids git's "already checked out" refusal when <branch> is the one you're currently on
git worktree add --detach /Users/<you>/Developer/ayunis-core-wt-<slug> origin/<branch>
# symlink secret envs + install
ln -sf "$REPO/ayunis-core-backend/.env" "$WT/ayunis-core-backend/.env"
ln -sf "$REPO/ayunis-core-frontend/.env" "$WT/ayunis-core-frontend/.env"
cd "$WT" && pnpm install && (cd ayunis-core-backend && pnpm run build:deps)
2. Bring up an ISOLATED slot
Never reuse or touch a slot that is already running — those are the user's. List them first and pick a free number (avoid 0/1 and anything running):
docker ps --filter name=ayunis-dev --format '{{.Names}}' # see which slots are up
cd "$WT" && ./dev up --slot <FREE_N> # e.g. 2, 3, 4 …
Port formula: port + slot×10 (slot 2 → backend 3020, frontend 3021, postgres 5452, minio 9020). See the dev-environment skill.
Fallback: ./dev up aborts on an unhealthy peripheral container
./dev up does docker compose up --wait, so one unhealthy peripheral (e.g. anonymize in a restart loop) makes it bail before starting the backend — even though postgres/minio/redis are healthy. Do not try to fix the container (see Guardrails). Start the app natively against the healthy infra instead:
-
Confirm core infra is healthy:
docker compose -p ayunis-dev-<N> ps(postgres, minio, redis Up/healthy). -
Read the actual published host ports (don't hardcode peripherals):
docker compose -p ayunis-dev-<N> ps --format '{{.Service}} {{.Publishers}}' -
Write
ayunis-core-backend/.env.dev(gitignored) mirroring what./devgenerates — see the block./dev'scmd_upwrites. Key vars:PORT,POSTGRES_PORT,MINIO_PORT,REDIS_PORT,SMTP_PORT,CODE_EXECUTION_SERVICE_URL,ANONYMIZE_SERVICE_URL,GOTENBERG_URL,CORS_ALLOWED_ORIGINS, and a freshMCP_ENCRYPTION_KEY=$(openssl rand -hex 32). Provider API keys come from the symlinked.env— do NOT put them in.env.dev.Critical:
./dev upgenerates random MinIO/Redis passwords beforecompose --waitbut only writes them to.env.devafter — so when it dies at--wait, the infra is running with passwords that were never persisted. Don't regenerate them (the backend won't match the live containers); recover the real ones from the running containers:Pipe them straight into
.env.dev— never echo secrets to the terminal (transcripts/CI logs are retained):P=ayunis-dev-<N>; ENV=ayunis-core-backend/.env.dev mi=$(docker inspect "$(docker compose -p $P ps -q minio)" -f '{{range .Config.Env}}{{println .}}{{end}}') { echo "MINIO_ACCESS_KEY=$(grep -m1 '^MINIO_ROOT_USER=' <<<"$mi" | cut -d= -f2-)" echo "MINIO_SECRET_KEY=$(grep -m1 '^MINIO_ROOT_PASSWORD=' <<<"$mi" | cut -d= -f2-)" docker inspect "$(docker compose -p $P ps -q redis)" -f '{{range .Config.Env}}{{println .}}{{end}}' | grep -m1 '^REDIS_PASSWORD=' } >> "$ENV"Without these three the backend can't reach MinIO/Redis. (
MINIO_ROOT_USER/MINIO_ROOT_PASSWORDmap toMINIO_ACCESS_KEY/MINIO_SECRET_KEY;REDIS_PASSWORDkeeps its name.) -
cd ayunis-core-backend && pnpm run migration:run:dev -
Backend (from
ayunis-core-backend):pnpm run start:dev(run_in_background). Pollhttp://localhost:<BE>/api/healthuntil{"status":"healthy"}. -
Frontend — must run from
ayunis-core-frontend(Vite is a frontend-only dep and won't resolve from the backend dir or repo root):cd ../ayunis-core-frontend && VITE_API_BASE_URL=http://localhost:<BE>/api pnpm exec vite --port <FE>(run_in_background).
3. Seed
cd ayunis-core-backend && pnpm run seed:minimal:ts # idempotent
Login: admin@demo.local / admin (Admin + Super Admin). See seed-database. Note the seed's teams may belong to a different org — if you need a team in the admin's org, create one via POST /teams.
4. Drive the changed flow
Pick the layers the change touches. Prefer BOTH when the feature spans API + UI.
Backend behaviour — authenticated API
Use a cookie jar; assert response fields, don't just eyeball.
API=http://localhost:<BE>/api ; J=/tmp/qa.cookies ; rm -f $J
curl -sf -c $J -X POST $API/auth/login -H 'Content-Type: application/json' \
-d '{"email":"admin@demo.local","password":"admin"}' >/dev/null
# then -b $J on every subsequent call. Discover payload shapes from:
curl -sf $API/docs-json -o /tmp/dj.json # OpenAPI: paths, request/response schemas
Reproduce the spec: set up preconditions, toggle the thing, assert the observable outcome (e.g. create a thread and assert isAnonymous === true). Test both the positive and the control (off → off).
Frontend behaviour — headless browser
Use puppeteer-core (already a backend dependency) with the system Chrome — this is what works on this machine:
// node script, run from repo root
import puppeteer from '<repo>/node_modules/.pnpm/puppeteer-core@<ver>/node_modules/puppeteer-core/lib/esm/puppeteer/puppeteer-core.js';
const CHROME = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
const b = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox'] });
const page = await b.newPage();
page.on('console', m => m.type()==='error' && errs.push(m.text()));
// login (generous waits — auth cookie must land before navigating to a protected route):
await page.goto(`${BASE}/login`, {waitUntil:'networkidle2'});
await page.type("input[name='email']",'admin@demo.local'); await page.type("input[name='password']",'admin');
await Promise.all([page.click("button[type='submit']"), page.waitForNavigation({waitUntil:'networkidle2'}).catch(()=>{})]);
await new Promise(r=>setTimeout(r,2500));
Gotchas that bit us:
- Login timing: wait ~2.5s after submit before navigating, or the protected route bounces back to
/login. - Radix tabs: a synthetic
el.click()insidepage.evaluatedoes NOT switch the tab. Use a real element-handle click:await (await page.$$('button[role=tab]'))[i].click(), thenwaitForFunctionon the active[role=tabpanel][data-state=active]. - Screenshot (
fullPage: true) andReadit as evidence. Assert the concrete DOM node exists (e.g. a switch withid$='-anonymous'), and confirm interactions persist across reload (proves it hit the API). - Assert
console errors: none(ignore incidental 403/favicon noise).
Responsiveness (REQUIRED when the diff touches frontend layout/UI)
Drive the changed screen at each breakpoint and assert it doesn't break. The overflow check is the automatic fail signal — a screenshot alone won't catch a busted layout:
for (const [name, width] of [['mobile',375],['tablet',768],['desktop',1280]]) {
await page.setViewport({ width, height: 900, deviceScaleFactor: 2 });
await new Promise(r=>setTimeout(r,400)); // let CSS/reflow settle
const overflow = await page.evaluate(() =>
document.documentElement.scrollWidth - document.documentElement.clientWidth);
console.log(`${name} ${width}px horizontalOverflow=${overflow}px`); // expect ≤ 1
await page.screenshot({ path: `<scratchpad>/pr-media/resp-${name}.png`, fullPage: true });
}
- Fail if
overflow > 1at any breakpoint (horizontal scrollbar = broken layout). Report the width and px. - Eyeball each shot for the mobile essentials: no clipped controls, nav collapses to its hamburger/drawer, dialogs and tables stay usable.
- The three
resp-*.pngcount as evidence and can double as PR media (step 4b).
4b. Frontend PR media (when it adds review evidence)
Use the repository's Proportional Workflow to decide whether media materially helps reviewers evaluate the visible result. QA screenshots remain local evidence even when publication adds no value.
When PR media is warranted, load the pr-media skill and follow its current publication workflow. Capture only the affected states and viewports. Add a GIF only when an interaction, transition, or multi-step flow cannot be judged from static screenshots. Do not duplicate the publishing implementation here; pr-media is the source of truth for its branch naming, automation, and verification.
5. Report
Present the acceptance-criteria checklist, each ✅/❌ with its evidence (asserted values, screenshot path). If anything failed, say so plainly with the observed vs expected — do not soften it. This is the whole point. When PR media was warranted, confirm the pr-media workflow published and verified it.
6. Tear down — leave the machine exactly as found
Always, even on failure:
# stop the native processes YOU started (use TaskStop on the background task ids)
cd "$WT" && ./dev down --slot <N> # slot you brought up — NEVER a pre-existing one
git -C "$WT" restore packages/*/dist # build:deps rebuilds these; don't leave them dirty
rm -f "$WT"/ayunis-core-backend/.env.dev
cd "$REPO" && git worktree remove "$WT" --force
Verify: docker ps --filter name=ayunis-dev shows only the slots that were running before you started; the QA slot's ports are free; the main checkout is on its original branch.
Guardrails (from CLAUDE.md — non-negotiable)
- Never
kill/pkilla process you didn't start. Stop your own background tasks via TaskStop. - Never destructive Docker flags: no
down -v,volume rm,system prune. Onlyup/down/ps/logs/exec. - Never touch a pre-existing slot or other infra. If a slot's volume is stale (migration
42P07) or a container won't come up, describe it and ask — don't fix it. Just pick a different free slot, or use the native-start fallback against healthy infra. - If the environment is broken in a way the fallback can't route around, stop and report — don't escalate fixes.
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/pre-merge-behaviour-qa-in-running-app-mts7ikdy/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/pre-merge-behaviour-qa-in-running-app-mts7ikdy' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/pre-merge-behaviour-qa-in-running-app-mts7ikdy/download' --output '.skill-downloads/pre-merge-behaviour-qa-in-running-app-mts7ikdy/SKILL.md.part' && mv '.skill-downloads/pre-merge-behaviour-qa-in-running-app-mts7ikdy/SKILL.md.part' '.skill-downloads/pre-merge-behaviour-qa-in-running-app-mts7ikdy/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/pre-merge-behaviour-qa-in-running-app-mts7ikdy/SKILL.md' && test -s '.skill-downloads/pre-merge-behaviour-qa-in-running-app-mts7ikdy/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/qa' && cp -R '.skill-downloads/pre-merge-behaviour-qa-in-running-app-mts7ikdy' '.claude/skills/qa'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/pre-merge-behaviour-qa-in-running-app-mts7ikdy'openskills read 'pre-merge-behaviour-qa-in-running-app-mts7ikdy'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/pre-merge-behaviour-qa-in-running-app-mts7ikdy/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.
Comparaison de performances ClickHouse
Testing
Évaluez les résultats de tests de performance ClickHouse, analysez les régressions de PR, comparez avec l'historique et vérifiez la fiabilité des mesures.
Analyse des rapports de performance CI
Testing
Analyse les rapports de comparaison de performance CI pour une PR ClickHouse. Recoupe l'historique master pour distinguer les vraies régressions des tests instables.
Méthode Santa
Testing
Vérification adversarial multi-agents avec deux réviseurs indépendants. La sortie est vérifiée deux fois et corrigée itérativement jusqu'à ce que les deux acceptent avant publication.