agentleFS
me2reshme2resh/apexyard/.claude/skills/launch-check/SKILL.md

launch-check

Skill542 starsChanged 11 days agoMITView on GitHub

Production readiness audit — 10-dimension go/no-go sweep (security, a11y, compliance, analytics, SEO, GEO, perf, monitoring, docs, behaviour-quality).

We split this skill into the rules it gives an agent

49 rules. 0 are common, 28 are distinctive, and 21 are very specific to this project.

  • 0

    Common

    Most of the 4 edited versions we read keep these.

  • 28

    Distinctive

    This author's own choices. Worth a look.

  • 21

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read .claude/rules/writing-standard.md.

    line 11
  2. Very specific:

    When a dimension shows WARN or FAIL, tell the user: *"For a detailed analysis, run /threat-model"* (or the relevant expert skill).

    line 41
  3. Very specific:

    Run npm audit / pip audit / equivalent for dependency vulnerabilities

    line 91
  4. Distinctive:

    Check auth flow: is there authentication?

    line 93
  5. Distinctive:

    Check for common OWASP risks: SQL/NoSQL injection surfaces, XSS vectors (dangerouslySetInnerHTML, v-html), CSRF protection

    line 94
  6. Distinctive:

    Check HTTPS enforcement, CORS configuration, security headers

    line 95
  7. Distinctive:

    PASS if: no critical vulns, auth uses a reputable provider, no hardcoded secrets, basic OWASP surface clean.

    line 97
  8. Very specific:

    Check for semantic HTML: <main>, <nav>, <header>, <footer>, heading hierarchy (h1 → h2 → h3)

    line 106
  9. Very specific:

    Check for aria-label on interactive elements without visible text

    line 107
  10. Distinctive:

    Check color contrast: look for low-contrast color combinations in CSS/theme files

    line 108
  11. Very specific:

    Check keyboard navigation: are there onClick handlers without corresponding onKeyDown?

    line 109
  12. Very specific:

    Check for <html lang="..."> attribute

    line 110
  13. Distinctive:

    PASS if: all images have alt text, semantic HTML used, no obvious contrast issues.

    line 112
  14. Distinctive:

    FAIL if: no cookie consent on a site that uses cookies, or no privacy policy on a user-facing app.

    line 128
  15. Distinctive:

    Check for event tracking calls (gtag, track, capture, etc.)

    line 135
  16. Distinctive:

    Check for a config file or environment variable pointing to an analytics dashboard

    line 136
  17. Distinctive:

    If no analytics at all and the project is user-facing: that's a finding

    line 137
  18. Very specific:

    Check for <title> and <meta name="description"> on key pages

    line 147
  19. Very specific:

    Check for <meta property="og:title">, og:description, og:image (Open Graph)

    line 148
  20. Very specific:

    Check for sitemap.xml at the expected location

    line 149
  21. Very specific:

    Check for canonical URLs (<link rel="canonical">)

    line 151
  22. Distinctive:

    Check for structured data (JSON-LD, schema.org)

    line 152
  23. Distinctive:

    FAIL if: no title/description on the main page, or no robots.txt.

    line 156
  24. Very specific:

    If there's a build output: check bundle size (look for dist/, build/, .next/)

    line 178
  25. Distinctive:

    Check for image optimization: are images served as WebP/AVIF?

    line 179
  26. Very specific:

    Check for lazy loading on below-the-fold images (loading="lazy")

    line 180
  27. Distinctive:

    Check for code splitting (dynamic imports, React.lazy)

    line 181
  28. Distinctive:

    If there's a Lighthouse config or Web Vitals tracking: check the targets

    line 182
  29. Very specific:

    Check for a health check endpoint (/health, /healthz, /api/health)

    line 193
  30. Distinctive:

    Check for alerting configuration (PagerDuty, OpsGenie, CloudWatch Alarms, or alerting rules in code)

    line 194
  31. Very specific:

    Check for a runbook or incident response doc (docs/runbook.md, docs/incident-response.md)

    line 195
  32. Distinctive:

    Check for logging configuration (structured logging, log levels)

    line 196
  33. Distinctive:

    FAIL if: no error tracking at all on a production app, or no health endpoint.

    line 200
  34. Distinctive:

    Check for staleness: compare README last-modified date with recent code changes

    line 211
  35. Distinctive:

    FAIL if: no README, or README is the default GitHub template.

    line 215
  36. Very specific:

    Read .claude/project-config.json → mutation.threshold (default 60% from .claude/project-config.defaults.json)

    line 221
  37. Very specific:

    Check whether a recent mutation report exists at projects/<name>/quality/mutation-<YYYY-MM-DD>.md (within the last 30 days)

    line 222
  38. Distinctive:

    If the latest report's score < threshold → WARN (not FAIL — mutation is a leading indicator, not a launch blocker)

    line 223
  39. Very specific:

    If mutation.runner is null or the project's primary language has no recognised mutation runner installed: auto-PASS (the audit doesn't …

    line 225
  40. Distinctive:

    FAIL if: never — mutation testing is advisory, not a launch blocker.

    line 229
  41. Very specific:

    If invoked with an argument, use that path.

    line 237
  42. Distinctive:

    Read the project's structure to determine what checks apply:

    line 241
  43. Distinctive:

    Run the checks listed above (grep, file existence, SDK detection)

    line 252
  44. Distinctive:

    Write a one-line finding for the table

    line 254
  45. Very specific:

    If the Workflow tool is unavailable in the current Claude Code installation, say so in one line and fall back to the serial Steps 3–5.

    line 355
  46. Distinctive:

    After the verdict table, persist this run to the project's history store via the shared audit-history lib and append a trend section if …

    line 359
  47. Distinctive:

    Read-only mode that produces just the trend section (no full audit, no per-dimension grep).

    line 455
  48. Very specific:

    Call audit_render_trend "<project-name>" "launch-check" 5.

    line 460
  49. Distinctive:

    Do NOT run the 9-dimension sweep in this mode.

    line 463

“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

/launch-check — Production Readiness Audit

Read .claude/rules/writing-standard.md. Use the controlled technical writing profile for the audit report. Lead with the verdict and next action; keep findings below it.

Runs a 10-dimension sweep against a project and outputs a one-page verdict. Designed for milestone boundaries — epic completion, release cuts, launch prep — not per-PR use.

Invoke from the project's workspace directory (cd workspace/<project>/) or pass the path as an argument: /launch-check workspace/my-app.

Three modes:

  • /launch-check [project-path] — full audit (default). Runs all 10 dimensions serially, persists results to the per-project history store, and renders a "Trend (last 5 runs)" section when ≥ 2 prior runs exist.
  • /launch-check trend [project-path] — read-only trend report. Renders just the trend section from existing run files. Useful for "are we trending up?" without burning the audit cost. See § "Trend-only mode" below.
  • /launch-check --workflow [project-path] — opt-in parallel + adversarially-verified audit. Fans the 10 dimensions out concurrently (one agent each), independently verifies every FAIL/WARN finding before it lands in the verdict, then synthesizes the same table and persists via the same history store. Faster wall-clock and fewer false positives; costs more tokens (spawns ~10–20 agents). Same output, same trend continuity — see § "Workflow mode" below.

Deep-dive companions

Each dimension has a dedicated expert skill for when you need to go deeper than the one-line summary. The launch check is the overview; the expert skills are the investigation.

DimensionQuick checkDeep dive
Security/launch-check row 1/threat-model — full STRIDE threat modelling exercise
Accessibility/launch-check row 2/accessibility-audit — WCAG 2.1 AA compliance audit
Compliance/launch-check row 3/compliance-check — GDPR + ePrivacy analysis
Analytics/launch-check row 4/analytics-audit — event taxonomy and funnel coverage
SEO/launch-check row 5/seo-audit — technical SEO against Google best practices
Generative-engine/launch-check row 6/geo-audit — LLM/agent discoverability (GEO + AEO), llms.txt, AGENTS.md, AI-crawler directives, JSON-LD citation grounding
Performance/launch-check row 7/performance-audit — bundle, images, Core Web Vitals
Monitoring/launch-check row 8/monitoring-audit — observability and incident readiness
Documentation/launch-check row 9/docs-audit — Diataxis framework completeness
Behaviour quality/launch-check row 10/mutation-test — mutation-testing sensor; measures whether the test suite constrains behaviour, not just executes lines

When a dimension shows WARN or FAIL, tell the user: "For a detailed analysis, run /threat-model" (or the relevant expert skill).

Output format

The output should be scannable in 10 seconds. One table, one verdict, one blockers list:

LAUNCH CHECK — <project> @ <sha> (<date>)

| #  | Dimension          | Status | Finding                              |
|----|--------------------|--------|--------------------------------------|
| 1  | Security           | PASS   | No critical vulns, auth flow ok      |
| 2  | Accessibility      | WARN   | 3 images missing alt text            |
| 3  | Compliance         | FAIL   | No cookie consent banner detected    |
| 4  | Analytics          | PASS   | GA4 configured, 12 events tracked    |
| 5  | SEO                | WARN   | Missing og:image on 2 pages          |
| 6  | Generative-engine  | WARN   | No llms.txt, AGENTS.md missing       |
| 7  | Performance        | PASS   | Bundle < 200KB, LCP < 2.5s          |
| 8  | Monitoring         | FAIL   | No health check endpoint found       |
| 9  | Documentation      | PASS   | README updated this week             |
| 10 | Behaviour quality  | WARN   | Mutation score 54% (threshold 60%)   |

Verdict: CONDITIONAL GO (2 failures need resolution)

Blocking:
  - [ ] Add cookie consent banner (compliance)
  - [ ] Add /health endpoint (monitoring)

Warnings (non-blocking, address before next launch):
  - [ ] Add alt text to 3 images (accessibility)
  - [ ] Add og:image to landing and pricing pages (SEO)

Do NOT dump hundreds of lines. Each dimension gets ONE row in the table. Details go in follow-up messages only if the user asks "tell me more about the security findings" — not upfront.

Verdict logic

ConditionVerdict
All dimensions PASSGO — ready to launch
Some WARN, zero FAILGO with warnings — launch is safe, address warnings before next milestone
Any FAILCONDITIONAL GO — resolve the blocking items before launch
3+ FAIL or any critical security findingNO-GO — significant gaps, not launch-ready

The 9 dimensions

1. Security

What to check:

  • Run npm audit / pip audit / equivalent for dependency vulnerabilities
  • Grep for hardcoded secrets, API keys, tokens (same patterns as check-secrets.sh but against the full codebase, not just staged files)
  • Check auth flow: is there authentication? Is it using a reputable provider (Cognito, Auth0, Firebase Auth)? Are tokens stored securely (httpOnly cookies, not localStorage)?
  • Check for common OWASP risks: SQL/NoSQL injection surfaces, XSS vectors (dangerouslySetInnerHTML, v-html), CSRF protection
  • Check HTTPS enforcement, CORS configuration, security headers

PASS if: no critical vulns, auth uses a reputable provider, no hardcoded secrets, basic OWASP surface clean. WARN if: medium vulns in dependencies, or missing security headers. FAIL if: critical vuln, hardcoded secrets found, no auth on a user-facing app, or obvious injection surface.

2. Accessibility

What to check:

  • Grep for <img tags without alt attributes
  • Check for semantic HTML: <main>, <nav>, <header>, <footer>, heading hierarchy (h1 → h2 → h3)
  • Check for aria-label on interactive elements without visible text
  • Check color contrast: look for low-contrast color combinations in CSS/theme files
  • Check keyboard navigation: are there onClick handlers without corresponding onKeyDown?
  • Check for <html lang="..."> attribute

PASS if: all images have alt text, semantic HTML used, no obvious contrast issues. WARN if: a few missing alt texts, or minor semantic gaps. FAIL if: systematic missing alt text, no semantic HTML at all, or no lang attribute.

3. Compliance

What to check:

  • Cookie consent: grep for cookie-consent libraries (cookieconsent, react-cookie-consent, etc.) or a consent banner component
  • Privacy policy: check for a /privacy route or privacy-policy page
  • Terms of service: check for a /terms route
  • GDPR: if the app collects user data, is there a data deletion mechanism? Check for a "delete account" route or endpoint
  • Data retention: is there a documented policy? (Can be in docs/ or in the privacy policy)

PASS if: cookie consent present, privacy policy exists, terms exist. WARN if: privacy policy exists but no data deletion mechanism. FAIL if: no cookie consent on a site that uses cookies, or no privacy policy on a user-facing app.

4. Analytics

What to check:

  • Grep for analytics SDK initialization (Google Analytics/GA4, Mixpanel, Amplitude, PostHog, Plausible)
  • Check for event tracking calls (gtag, track, capture, etc.)
  • Check for a config file or environment variable pointing to an analytics dashboard
  • If no analytics at all and the project is user-facing: that's a finding

PASS if: analytics SDK configured and events are being tracked. WARN if: SDK configured but few/no custom events (only page views). FAIL if: user-facing app with zero analytics. (Non-user-facing like CLIs/libraries: auto-PASS.)

5. SEO

What to check:

  • Check for <title> and <meta name="description"> on key pages
  • Check for <meta property="og:title">, og:description, og:image (Open Graph)
  • Check for sitemap.xml at the expected location
  • Check for robots.txt
  • Check for canonical URLs (<link rel="canonical">)
  • Check for structured data (JSON-LD, schema.org)

PASS if: title + description + OG tags on main pages, sitemap exists, robots.txt exists. WARN if: missing OG tags on some pages, or no structured data. FAIL if: no title/description on the main page, or no robots.txt. (Non-web projects: auto-PASS.)

6. Generative-engine (LLM/agent discoverability)

What to check (quick scan — full deep-dive lives in /geo-audit):

  • llms.txt and llms-full.txt at the site root
  • AGENTS.md at the repo root
  • AI-crawler directives in robots.txt (does it name GPTBot, ClaudeBot, PerplexityBot, etc.?)
  • JSON-LD citation metadata on article-shaped pages (author, dateModified, datePublished, publisher)
  • Per-page token count for the largest docs page (heuristic: char_count / 4 — flag pages over 25K)

Covers two related sub-scopes: GEO (LLM citations — ChatGPT, Claude, Perplexity, Gemini) and AEO (coding-agent consumption — Claude Code, Cursor, Aider, Cline). Both consume the same artefacts, so they share one row at the milestone-boundary level. For the bucket-by-bucket breakdown, run /geo-audit.

PASS if: llms.txt present, AGENTS.md present with sandbox + MCP sections, JSON-LD citation metadata on key pages. WARN if: some artefacts missing (e.g. AGENTS.md exists but lacks sandbox links). FAIL if: no llms.txt AND no AGENTS.md AND no citation JSON-LD on a content-heavy site. (Non-web projects: auto-PASS.)

7. Performance

What to check:

  • If there's a build output: check bundle size (look for dist/, build/, .next/)
  • Check for image optimization: are images served as WebP/AVIF? Are there large unoptimized images (> 500KB)?
  • Check for lazy loading on below-the-fold images (loading="lazy")
  • Check for code splitting (dynamic imports, React.lazy)
  • If there's a Lighthouse config or Web Vitals tracking: check the targets

PASS if: bundle under 300KB gzipped, images optimized, lazy loading present. WARN if: bundle 300-500KB, or a few large images. FAIL if: bundle > 1MB, or systematically unoptimized images. (Non-web: auto-PASS.)

8. Monitoring

What to check:

  • Grep for error tracking SDK (Sentry, Datadog, Bugsnag, LogRocket)
  • Check for a health check endpoint (/health, /healthz, /api/health)
  • Check for alerting configuration (PagerDuty, OpsGenie, CloudWatch Alarms, or alerting rules in code)
  • Check for a runbook or incident response doc (docs/runbook.md, docs/incident-response.md)
  • Check for logging configuration (structured logging, log levels)

PASS if: error tracking configured, health endpoint exists, some alerting in place. WARN if: error tracking but no health endpoint, or no runbook. FAIL if: no error tracking at all on a production app, or no health endpoint.

9. Documentation

What to check:

  • README.md exists and is not the default template
  • README has: project description, setup instructions, how to run locally, how to deploy
  • API documentation: if there's an API, is it documented? (OpenAPI spec, Swagger, or doc comments)
  • Deployment guide: how to deploy to staging/production
  • Changelog: is there a CHANGELOG.md or are releases documented?
  • Check for staleness: compare README last-modified date with recent code changes

PASS if: README has setup + run + deploy instructions, and was updated recently. WARN if: README exists but is stale (> 30 days behind code changes), or API undocumented. FAIL if: no README, or README is the default GitHub template.

10. Behaviour quality (mutation testing)

What to check (quick gate — full deep-dive lives in /mutation-test):

  • Read .claude/project-config.json → mutation.threshold (default 60% from .claude/project-config.defaults.json)
  • Check whether a recent mutation report exists at projects/<name>/quality/mutation-<YYYY-MM-DD>.md (within the last 30 days)
  • If the latest report's score < threshold → WARN (not FAIL — mutation is a leading indicator, not a launch blocker)
  • If no recent report exists, offer to dispatch to /mutation-test for a fresh run, but do NOT auto-run it during /launch-check. A mutation audit takes 20–40 minutes on a medium codebase — too slow to fold into the milestone-boundary sweep silently. The dimension reports WARN with the finding "no recent mutation report (run /mutation-test for a fresh number)".
  • If mutation.runner is null or the project's primary language has no recognised mutation runner installed: auto-PASS (the audit doesn't apply — same shape as SEO auto-PASS for non-web projects). Surface the gap as INFO in the finding column.

PASS if: recent mutation report exists AND score ≥ threshold. WARN if: recent mutation report exists BUT score < threshold, OR no recent report (operator should run /mutation-test). FAIL if: never — mutation testing is advisory, not a launch blocker. See AgDR-0045 for the rationale.

Auto-PASS for projects without a mutation runner installed (or where the language has no recognised dispatch — e.g. Rust at v1).

Process

Step 1: Determine the project

If invoked with an argument, use that path. Otherwise, use the current working directory. Verify it's a git repo with source code (not the ops repo itself — the launch check is for managed projects, not for apexyard).

Step 2: Quick scan

Read the project's structure to determine what checks apply:

  • Is it a web app? (has index.html, React/Vue/Svelte/Next.js markers) → all 9 dimensions
  • Is it an API only? (has routes/endpoints but no frontend) → skip accessibility, SEO, generative-engine, performance (auto-PASS)
  • Is it a CLI/library? → skip accessibility, compliance, analytics, SEO, generative-engine, performance (auto-PASS on those)
  • Is it a mobile app? → adjust accessibility checks for mobile, skip SEO, skip generative-engine

Step 3: Run each applicable dimension

Go through each dimension in order. For each:

  1. Run the checks listed above (grep, file existence, SDK detection)
  2. Classify as PASS / WARN / FAIL based on the criteria
  3. Write a one-line finding for the table

Do NOT spend more than 30 seconds per dimension. The checks should be quick grepping and file scanning, not deep code review. Deep dives happen in the dedicated companion skill (/seo-audit, /geo-audit, /threat-model, etc.) — not inside the initial sweep.

Step 4: Compile the verdict

Count PASS/WARN/FAIL, apply the verdict logic, format the output table.

Step 5: Output

Print the table exactly as shown in the "Output format" section above. Then continue to Step 6 to persist the run and render the trend section.

Do NOT auto-create tickets for the findings. Offer: "Want me to create tickets for the blocking items?" — but let the user decide. The launch check is advisory.

Workflow mode (--workflow) — opt-in parallel + adversarial verify

When invoked as /launch-check --workflow [project-path], replace the serial Steps 3–5 with a single Workflow tool invocation that fans the 10 dimensions out concurrently and adversarially verifies the findings, then resume at Step 6 (persist) unchanged. Steps 1–2 (resolve project + detect stack) and Step 6 (persist + trend) are identical to the default path — only the evaluation of the 10 dimensions changes from serial to fanned-out.

Why a Workflow here (and why opt-in)

The 10 dimensions are independent and read-only — no shared writes, no cross-dimension data dependency, no human gate. That's the canonical fan-out shape: evaluate all 10 in parallel, then have an independent skeptic try to refute each FAIL/WARN before it reaches the verdict (the false-positive killer). The trade-off is token cost — a workflow run spawns ~10–20 agents versus one serial pass — so this is opt-in, never the default. Rationale: AgDR-0055.

Prerequisite: the Workflow tool requires explicit opt-in to multi-agent orchestration. The --workflow flag is that opt-in — when the operator passes it, author and run the workflow below. Without the flag, run the serial path and do NOT invoke Workflow.

The canonical workflow script

Author and run this via the Workflow tool (adapt the project name / stack-skip set from Steps 1–2). The dimension prompts reuse the exact PASS/WARN/FAIL criteria from "The 9 dimensions" / row-10 below — do not invent new criteria. Auto-PASS the not-applicable dimensions per the existing rules (e.g. SEO/GEO/perf auto-PASS for a non-web project, analytics auto-PASS for a CLI/library) by passing them as already-resolved rather than spawning an agent.

export const meta = {
  name: 'launch-check',
  description: 'Parallel 10-dimension production-readiness audit with adversarial verification',
  phases: [
    { title: 'Evaluate', detail: 'one agent per applicable dimension' },
    { title: 'Verify', detail: 'adversarially refute each FAIL/WARN finding' },
  ],
}

// args = { projectPath, projectName, dimensions: [{ key, label, prompt, applicable }] }
// `prompt` carries that dimension's "What to check" + PASS/WARN/FAIL criteria verbatim
// from this SKILL. Non-applicable dimensions are pre-resolved to PASS and skipped.

const DIM_SCHEMA = {
  type: 'object',
  required: ['key', 'status', 'score', 'finding'],
  properties: {
    key:     { type: 'string' },
    status:  { enum: ['PASS', 'WARN', 'FAIL'] },
    score:   { type: 'integer' },            // 0-100, same scale render-trend.sh plots
    finding: { type: 'string' },             // one-line, fits the verdict-table cell
    evidence:{ type: 'string' },             // file:line or command output backing the status
  },
}
const VERDICT_SCHEMA = {
  type: 'object',
  required: ['key', 'upheld', 'reason'],
  properties: {
    key:    { type: 'string' },
    upheld: { type: 'boolean' },             // false = the skeptic refuted the WARN/FAIL
    reason: { type: 'string' },
  },
}

const applicable = args.dimensions.filter(d => d.applicable)
const preResolved = args.dimensions.filter(d => !d.applicable)
  .map(d => ({ key: d.key, status: 'PASS', score: 100, finding: 'N/A for this project type' }))

// Phase 1+2 pipelined: each dimension verifies as soon as its evaluation returns,
// rather than waiting for the slowest dimension (no barrier).
const evaluated = await pipeline(
  applicable,
  d => agent(d.prompt, { label: `eval:${d.key}`, phase: 'Evaluate', schema: DIM_SCHEMA, agentType: d.agentType }),
  (res, d) => {
    if (!res) return null
    if (res.status === 'PASS') return res            // nothing to refute
    // Adversarially verify only WARN/FAIL — the findings that move the verdict.
    return agent(
      `Adversarially REFUTE this ${d.label} finding for ${args.projectName}: "${res.finding}" ` +
      `(evidence: ${res.evidence || 'none'}). Default upheld=false if you cannot independently confirm it. ` +
      `Re-check the codebase yourself.`,
      { label: `verify:${d.key}`, phase: 'Verify', schema: VERDICT_SCHEMA },
    ).then(v => {
      // A refuted FAIL/WARN is downgraded to PASS (the false-positive cut).
      if (v && v.upheld === false) return { ...res, status: 'PASS', finding: `${res.finding} (refuted on verify: ${v.reason})` }
      return res
    })
  },
)

return { results: [...preResolved, ...evaluated.filter(Boolean)] }

After the workflow returns

  1. Take results[] (one row per dimension, each with status / score / finding).
  2. Count PASS/WARN/FAIL, apply the same verdict logic ("Verdict logic" table above) — go / go-with-warnings / conditional-go / no-go.
  3. Print the same verdict table as the serial path (Step 5 "Output format").
  4. Persist via Step 6 unchanged — build the same superset payload (scores{} from each row's score, findings[] from the WARN/FAIL rows), call audit_run_persist, then audit_render_trend. The history store, schema, and render-trend.sh chart are identical to a serial run, so trend continuity is preserved across serial↔workflow runs.

Degrade gracefully

If the Workflow tool is unavailable in the current Claude Code installation, say so in one line and fall back to the serial Steps 3–5. The audit must still complete — --workflow is an optimisation, not a hard dependency.

Step 6: Persist the run + render the trend

After the verdict table, persist this run to the project's history store via the shared audit-history lib and append a trend section if there are ≥ 2 prior runs.

This step was refactored as part of #218 to consume _lib-audit-history.sh (the same lib that powers /threat-model, /security-review, and the other audit skills). Behaviour is preserved: the JSON schema is the same superset shape, the chart is rendered by the same render-trend.sh (dispatched from inside the lib), and adopters' existing run-history at the legacy path is still picked up by the trend renderer.

6a. Resolve project name + score + verdict

<project-name> is the project's registered name in apexyard.projects.yaml. If the project isn't registered (e.g. someone runs /launch-check on a directory that's never been onboarded), use the basename of the project path; tell the operator to /handover it for cross-machine trend continuity.

The headline score is the unweighted mean of scores.*, rounded to int. The verdict is one of go / go-with-warnings / conditional-go / no-go (launch-check-specific four-state vocabulary, preserved from the existing skill).

6b. Build payload + body, persist via the lib

The lib's audit_run_persist accepts arbitrary stdin JSON and augments it with top-level ts / dimension / verdict / score fields, then writes both a JSON file and an MD file. For launch-check the JSON payload is a SUPERSET — it includes the legacy scores{} map (for render-trend.sh to plot) AND a derived findings[] array (for the canonical schema):

source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-audit-history.sh"

# Map each scored dimension to a Finding for the canonical schema.
# Severity from score: ≥85 info, 70-84 low, 55-69 medium, 40-54 high, <40 critical.
payload=$(mktemp); cat > "$payload" <<'EOF'
{
  "schema_version": 1,
  "branch": "main",
  "commit": "abc1234",
  "scores": {
    "security": 88, "accessibility": 94, "compliance": 76,
    "analytics": 90, "seo": 87, "generative_engine": 64,
    "performance": 68, "monitoring": 83, "docs": 91
  },
  "top_risks": ["No cookie consent banner", "Missing /health endpoint"],
  "findings": [
    {"id": "compliance",  "severity": "low",    "status": "open", "summary": "compliance score 76 — cookie consent banner missing"},
    {"id": "performance", "severity": "medium", "status": "open", "summary": "performance score 68 — bundle > 250KB"}
  ]
}
EOF

# Body: the human-readable per-run summary launch-check already produces.
body=$(mktemp); cat > "$body" <<'EOF'
## Launch-check verdict: conditional-go

| Dimension       | Score | Status |
|-----------------|-------|--------|
| Security        | 88    | PASS   |
| Accessibility   | 94    | PASS   |
| Compliance      | 76    | WARN   |
| ...             | ...   | ...    |

## Top risks

1. No cookie consent banner
2. Missing /health endpoint

## Recommendations

(...)
EOF

ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
audit_run_persist "<project-name>" "launch-check" "$ts" "conditional-go" 84 "$body" < "$payload"
rm -f "$payload" "$body"

audit_run_persist writes:

  • projects/<name>/audits/launch-check/runs/<ts>.json — JSON with everything the lib added on top of the payload (top-level ts, dimension: launch-check, verdict, score, schema_version) AND everything in the original payload (scores{}, branch, commit, top_risks[], findings[], derived stats{})
  • projects/<name>/audits/launch-check/<ts>.md — frontmatter + the body above
  • projects/<name>/audits/launch-check/.gitignore — driven by the per-dim .audit-history-tracked marker presence

JSON schema is a SUPERSET. The canonical fields (ts, dimension, verdict, score, findings[], stats{}, schema_version) coexist with the launch-check-specific fields (scores{}, branch, commit, top_risks[]). The legacy render-trend.sh reads ts / scores / verdict and works unchanged. The new generic audit_render_trend reads ts / score / verdict and works for the rest of the audit family.

6c. Render the trend section

audit_render_trend "<project-name>" "launch-check" 5
  • The lib internally dispatches to render-trend.sh for the launch-check dimension — byte-equal output to the pre-#218 chart (mean of scores.* on Y-axis, score-delta notes, ASCII chart).
  • Crucially, the lib's audit_run_list merges JSON from the canonical path (projects/<name>/audits/launch-check/runs/) AND the legacy path (projects/<name>/launch-check/runs/) — so adopters' existing history continues to render across the migration. No mv required.
  • Output is a markdown trend block (heading + table + ASCII chart). Append it to this run's MD artefact and to the chat output.

6d. Opt-in commit (history-tracked marker)

The per-dimension marker (.audit-history-tracked) sits inside the dimension's audit dir and controls whether runs/*.json files are gitignored:

# Opt in to commit launch-check history
touch projects/<name>/audits/launch-check/.audit-history-tracked

The lib re-evaluates the marker on every persist; toggling is free. The MD artefact at <dim_dir>/<ts>.md is committed regardless — that's the durable human-readable artefact.

Migration note for adopters with existing history: the legacy marker at projects/<name>/launch-check/.launch-check-history-tracked continues to apply to that path's runs/. To consolidate trees, mv projects/<name>/launch-check/* projects/<name>/audits/launch-check/ after backing up; the lib's renderer will read the consolidated tree on the next run. Consolidation is optional — the read-merge happens transparently if you leave both trees in place.

Trend-only mode — /launch-check trend [project-path]

Read-only mode that produces just the trend section (no full audit, no per-dimension grep). Useful for "are we trending up?" without re-running the costly sweep.

Process:

  1. Source _lib-audit-history.sh (same as Step 6).
  2. Call audit_render_trend "<project-name>" "launch-check" 5. The lib reads both the canonical path AND the legacy path; if the merged set has < 2 runs the call is silent and you tell the operator there's no trend yet.
  3. Print the renderer's output.

Do NOT run the 9-dimension sweep in this mode. Do NOT write any new JSON. This mode is purely for reviewing existing history.

Rules

  1. Scannable in 10 seconds. One table, one verdict, one blockers list. No walls of text.
  2. One row per dimension. Details on demand, not upfront.
  3. 30 seconds per dimension. Quick grep and file scanning, not deep code review.
  4. Auto-PASS non-applicable dimensions. A CLI doesn't need a cookie banner.
  5. Advisory, not blocking. The user decides go/no-go based on the findings. The skill does NOT block deploys mechanically.
  6. Don't create tickets unprompted. Offer to create them. Let the user decide.
  7. Run from the project directory. The skill checks workspace/<project>/, not the ops repo.
  8. Persist every run via the lib. Step 6 always calls audit_run_persist, regardless of opt-in commit state. The .audit-history-tracked marker only controls whether the JSON files are committed; the MD artefacts are committed unconditionally and persistence happens on every run.
  9. Resolve paths via the lib. audit_resolve_dir (inside _lib-audit-history.sh) calls portfolio_projects_dir for you — the SKILL doesn't need to source the portfolio helper directly any more.

Implementation notes

Persistence + trend logic ships as a shared shell helper that all audit skills consume:

FilePurpose
.claude/hooks/_lib-audit-history.shAudit-history library shared with /threat-model, /security-review, etc. (4 functions: audit_resolve_dir, audit_run_persist, audit_run_list, audit_render_trend)
render-trend.sh (this skill's dir)Legacy chart renderer for the launch-check dimension; the lib's audit_render_trend dispatches to it when the dimension is launch-check so the chart shape stays byte-equal across the #218 refactor

Design rationale:

  • ASCII chart vs Mermaid, JSON schema choice, opt-in commit marker: see docs/agdr/AgDR-0014-launch-check-trend-tracking.md
  • Audit-history lib shape, JSON+MD pair, launch-check backward-compat strategy: see docs/agdr/AgDR-0019-audit-artefact-persistence.md and docs/technical-designs/audit-artefact-persistence.md

Part of ApexYard — multi-project SDLC framework for Claude Code · MIT.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.