The site now presents one freelance developer on one page: hero, introduction, four typical cases (developer for a product, make an app reliable, automate a process, a website) with what you get, how it runs and what it costs, a process automation section, technology and project fit, how I work, two live references, questions and contact. - Hero and closing headline are drawn from running binary digits. - Each case explains its four steps with an animated drawing; the automation section shows example workflows with work travelling between systems; a pipeline run shows what a test catches before a release. - Contact is one email address, kept in parts and assembled in the browser so it never appears in the page source (src/domain/email.ts, spec first). - Imprint and privacy pages added. - The old site (blog, case studies, contact form, fixed-price pages, video rendering and related scripts) is removed. Its URLs redirect to the homepage. - The deploy smoke test checks the legal pages and the redirects instead of the removed case study. - Dev scripts use docker compose v2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.0 KiB
4.0 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 errorspnpm --filter @mintel/web test— unit specs (Vitest); 0 tests run counts as a failurepnpm --filter @mintel/web test:coverage—src/domainmust stay at 100%pnpm --filter @mintel/web test:mutation— Stryker onsrc/domain, ~2 min, must score 100pnpm --filter @mintel/web lint
How to work here (spec first)
- Write the behavior as concrete cases (inputs, outputs, edge cases, errors) as a
*.spec.ts. - Run it and confirm it fails for the right reason (missing behavior, not an import error).
- Implement the simplest clean solution, run again.
- Run typecheck, coverage and mutation. Survivors mean weak specs or dead code: fix one or the other.
- 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/imprintand/privacy. URLs of the previous site redirect to/(seenext.config.mjs). - The email address is kept in parts (
src/content/site.ts) and assembled in the browser byEmailLink. 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; passtodayetc. 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 tomain, or runreset --hard,clean,rm -rfwithout asking. - Edit
.gitea/workflows/*or deploy config without asking. - Upgrade
vitestpast 4.1.x:@stryker-mutator/vitest-runner10.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
tsconfigincludes.next/types: after deleting a route, stale generated files there breaktsc. Runnext typegenand remove the stale file.- Run only one Stryker process at a time; parallel runs share
.stryker-tmpand hang. - Stryker needs
pluginslisted explicitly instryker.config.json(pnpm layout) and ignores the large asset folders viaignorePatterns; keep them in sync when adding top-level folders. @mintel/*packages arelink:dependencies on a siblingat-mintelcheckout. The app itself now only uses@mintel/next-config; the other entries inpackage.jsonare leftovers from the old site.- Pushing
maindeploys to the testing environment; production is deployed from av*tag. - Git branches: work on
feat/*, never directly onmain.