seedproject-web/.claude/skills/qa/SKILL.md
Carlos Arias 56c1926b81 qa: QA agent — HTTP site testing with one-click Web Designer fixes
- migration 013: cja_qa_runs + cja_qa_findings
- runner: runQa (crawl pages/links/images/external/contact-probes/health/
  SEO/meta/sitemap/a11y), qaTriage (LLM summary via qa skill), /devconsole/qa/run
- qa-start/finish/routes.php CLI bridge
- adminqa controller (runs/findings/fix/ignore); fix() chains to the Web
  Designer queue; /admin/qa report page + nav link
- qa skill; contact form probed via honeypot (writes nothing)
2026-07-24 03:30:13 +00:00

68 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: qa
description: Quality-assurance testing for the Carlos Arias website (carlosarias.co). Use when summarising a QA run, triaging findings, deciding severity, or fixing an issue the QA agent found (broken link, dead image, form failure, SEO/meta gap, sitemap gap, missing alt text). Explains what the site's QA covers, how severe each kind of issue is, and the gotchas of testing this specific site.
---
# Skill: QA for carlosarias.co
The site is a **static Astro build** (content baked from `cja_projects` / `cja_changelog`
at build time) plus a small **PHP API** at `/api`. The only live runtime endpoints are
`POST /api/contact/submit` and `GET /api/health`; everything else is pre-rendered HTML. So
QA is HTTP-based: a crawler fetches pages and parses the served HTML — no browser, no JS
execution.
## What a QA run checks
| check_type | What it verifies | Default severity |
|---|---|---|
| `page` | Every route returns 200 with HTML | **error** if not 200 |
| `link` | Every internal `<a href>` resolves (200) | **error** |
| `image` | Every `<img src>` / og:image / icon loads | **error** |
| `external` | Off-site links (social, project `links.live`) reachable | **warning** |
| `form` | `/api/contact/submit` alive (honeypot probe → 200) + rejects bad input (→ 422) | error / warning |
| `health` | `/api/health` returns 200 and `db:"connected"` | **error** |
| `seo` | Each page has a `<title>` + meta description; canonical host is carlosarias.co; titles aren't duplicated | warning (error for wrong canonical host) |
| `sitemap` | Every route appears in `sitemap.xml` | **warning** |
| `a11y` | Images have `alt`; `<html lang>` is set | **warning** |
## Severity model
- **error** — the site is broken for a real visitor: a page/link/image 404s, the contact
form or API is down, or canonical URLs point at the wrong host. Fix promptly.
- **warning** — degraded but working: a missing meta description, a page absent from the
sitemap, missing alt text, or an external link that looks dead. Fix when convenient.
- **info** — notes, no action needed.
## Site-specific gotchas (important)
- **Contact form is honeypot-probed, never really submitted.** The form has a hidden
`company` field; if it's filled, the server returns `200 {received}` and writes **nothing**.
QA fills it on purpose so the "is the endpoint alive" probe leaves no `cja_contact` row and
sends no email. A separate probe sends a too-short `message` to confirm validation returns
422. Never treat these probes as real leads.
- **External links are warnings, not errors.** Sites like Instagram/LinkedIn frequently
return 403/429 to bots or time out — that is not proof the link is broken. Only a clear
404/410 or DNS failure is flagged, and only as a warning. Don't over-react.
- **Known sitemap gap:** `sitemap.xml` currently omits `/projects`, `/changelog`,
`/services`, `/resume`, `/faq`. These will show as `sitemap` warnings until
`app/src/pages/sitemap.xml.js` is updated to include them.
- **Duplicate titles** usually mean a page didn't set its own `<title>` and fell back to the
site default in `BaseLayout.astro`. The fix is a page-specific title/description prop.
- **Static build:** a content fix (e.g. a project's broken `links.live`) lives in the DB
(`cja_projects`) or a page's `.astro`, and only goes live after a rebuild — which the
console handles. Don't expect a DB edit alone to change the live page.
## Fixing a finding
Each finding carries a `fix_hint` — a ready-made instruction. When the admin turns a finding
into a Web Designer fix task, that hint becomes the task prompt. When you (as the Web
Designer) act on it: make the smallest correct change on the named page, stay on-brand (see
the `brand` skill), and let the console build/publish. Prefer fixing the source of a bad link
(the nav/footer/component or the `cja_projects` `links` value) over patching one instance.
## Writing the run summary
When asked to summarise a run: 24 sentences, plain English, for the site owner. Lead with
overall health ("clean" / "a few warnings" / "N errors need attention"), name the most
important thing to fix first, and say whether anything is urgent (a down form or API, a 404
on a linked page). No preamble, no restating every finding.