Files
mintel.me/CLAUDE.md
T
mmintelandClaude Opus 5.5 123ab8e354
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🏗️ Build (push) Successful in 6m28s
Build & Deploy / 🚀 Deploy (push) Successful in 17s
Build & Deploy / 🩺 Smoke Test (push) Successful in 4s
Build & Deploy / 🔔 Notify (push) Successful in 2s
feat: share image, metadata and icons that match the new site
The link preview used an old template with a blog description. The share
image is now a screenshot of the real hero (scripts/render-share-image.mjs),
served as Open Graph and Twitter image. Title and description come from one
place and are used by the page, the previews and the structured data. Adds an
apple touch icon and removes the old share-image template and its fonts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 23:33:02 +02:00

4.3 KiB

mintel.me

Personal website of a senior freelance engineer. Next.js 16 (App Router), pnpm monorepo. App lives in apps/web. Concept and scope: apps/web/plans/freelance-relaunch.md (read it first).

Commands (run from repo root)

  • pnpm --filter @mintel/web typecheck — must pass with zero errors
  • pnpm --filter @mintel/web test — unit specs (Vitest); 0 tests run counts as a failure
  • pnpm --filter @mintel/web test:coverage — src/domain must stay at 100%
  • pnpm --filter @mintel/web test:mutation — Stryker on src/domain, ~2 min, must score 100
  • pnpm --filter @mintel/web lint

How to work here (spec first)

  1. Write the behavior as concrete cases (inputs, outputs, edge cases, errors) as a *.spec.ts.
  2. Run it and confirm it fails for the right reason (missing behavior, not an import error).
  3. Implement the simplest clean solution, run again.
  4. Run typecheck, coverage and mutation. Survivors mean weak specs or dead code: fix one or the other.
  5. Report the exact commands and results. "Done" means they were run on the current code. Never weaken, skip or delete a test to make it pass.

Copy rules (client-facing pages)

  • Never mention AI as the owner's tool, and no internal method vocabulary (specs, TDD, mutation, coverage, pull requests). The automation service may name AI as part of what the client gets. Plain language.
  • Modest and factual: no promises the owner did not confirm, no superlatives. See the concept, section 1.
  • One page: everything lives on the homepage. The header has only "Get in touch", which jumps to the contact section (#contact). Contact is one email address: no calendar integration, no form. The only other pages are /imprint and /privacy. URLs of the previous site redirect to / (see next.config.mjs).
  • Title, description and link previews come from SITE_TITLE and SITE_DESCRIPTION in src/content/site.ts. The share image is a screenshot of the real hero: after changing the hero headline or its design, run pnpm --filter @mintel/web share-image and commit the two PNGs in app/(site)/.
  • The email address is kept in parts (src/content/site.ts) and assembled in the browser by EmailLink. Never write the full address into markup, metadata or copy.
  • The hero design (binary-digit headline) is approved: do not change its design.
  • Look at the result before reporting: take screenshots (desktop and mobile), do not ship blind.

Architecture rules

  • Functional core, imperative shell. Pure logic goes in apps/web/src/domain/ (no I/O, no clock, no randomness, no framework imports; pass today etc. in as arguments). Everything there is covered 100% and mutation-tested. Pages, routes and adapters stay thin and call into the core.
  • Parse at the boundary into precise types (Result, discriminated unions); no nullable state bags.
  • No any, @ts-ignore, ! non-null assertions or eslint-disable to silence errors. Fix the cause.

Do not

  • Read or edit .env* (only .env.example). Never print or log secrets.
  • Commit scratch files (tmp*, *.log, *.tmp, debug HTML, build output). They are gitignored; keep it so.
  • Use --no-verify, force-push, push to main, or run reset --hard, clean, rm -rf without asking.
  • Edit .gitea/workflows/* or deploy config without asking.
  • Upgrade vitest past 4.1.x: @stryker-mutator/vitest-runner 10.0.0 runs zero tests per mutant under vitest 5 and reports false survivors. Re-verify mutation results after any tooling bump.
  • Add dependencies for trivial things, or touch unrelated code (no drive-by refactors).

Gotchas

  • tsconfig includes .next/types: after deleting a route, stale generated files there break tsc. Run next typegen and remove the stale file.
  • Run only one Stryker process at a time; parallel runs share .stryker-tmp and hang.
  • Stryker needs plugins listed explicitly in stryker.config.json (pnpm layout) and ignores the large asset folders via ignorePatterns; keep them in sync when adding top-level folders.
  • @mintel/* packages are link: dependencies on a sibling at-mintel checkout. The app itself now only uses @mintel/next-config; the other entries in package.json are leftovers from the old site.
  • Pushing main deploys to the testing environment; production is deployed from a v* tag.
  • Git branches: work on feat/*, never directly on main.